feat:主应用修改

This commit is contained in:
wangzenghua 2025-01-25 02:44:29 +00:00
parent 7902143658
commit 70d1c91623
29 changed files with 669 additions and 477 deletions

View File

@ -1,9 +1,10 @@
# 开发环境
VITE_PORT = 9000
VITE_APP_NAME = 'daimp-front-main'
VITE_APP_TITLE = '数字农业产业管理平台'
VITE_APP_BASE_API = "https://mock.mengxuegu.com/mock/664ef7fee45d2156fa209ee4/api-qiankun"
VITE_APP_BASE_URL = 'http://192.168.18.158:9080'
VITE_APP_SUB_VUE = '//localhost:9526/sub-vue/'
VITE_APP_SUB_VUE = '//localhost:9526/sub-operation-service/'
VITE_APP_SUB_ADMIN = '//localhost:9527/sub-admin/'
VITE_APP_SUB_GAS = '//localhost:9528/suv-government-affairs-service/'

View File

@ -1,5 +1,6 @@
# 正式环境
VITE_APP_NAME = 'daimp-front-main'
VITE_APP_TITLE = '数字农业产业管理平台'
VITE_APP_BASE_API = ""
VITE_APP_BASE_URL = ''
VITE_APP_SUB_VUE = '//localhost:9526/sub-vue/'

View File

@ -1,34 +1,16 @@
<template>
<!-- <el-button v-for="item in microApps" :key="item.activeRule" type="primary" @click="gotoPage(item)">{{ item.name }}</el-button> -->
<router-view />
<div id="app">
<router-view />
</div>
</template>
<script setup name="App">
import { reactive, provide, nextTick } from 'vue';
import { microApps } from '@/micro/app';
import actions from '@/micro/actions';
const state = reactive({
isRouterAlive: true,
});
const gotoPage = (row) => {
const data = {};
switch (row.name) {
case 'sub-vue':
data.title = 'sub-vue';
break;
case 'sub-admin':
data.title = 'sub-admin';
break;
}
//
actions.setGlobalState({
data: data,
});
window.history.pushState({}, row.name, row.activeRule);
};
const reload = () => {
state.isRouterAlive = false;
nextTick(() => {

View File

@ -0,0 +1,46 @@
<template>
<el-dropdown popper-class="custom-table-operate">
<el-icon class="custom-table-operate__more"><More /></el-icon>
<template #dropdown>
<el-dropdown-menu v-if="!isEmpty(actions)">
<template v-for="item in actions" :key="item.name">
<el-dropdown-item v-if="onPermission(item)" @click="item.event(data)">
<el-button :type="item.type ?? 'primary'" :icon="item.icon" :size="item.size" :disabled="item.disabled" text>
{{ item.name }}
</el-button>
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup name="custom-table-operate">
import { isEmpty } from '@/utils';
const props = defineProps({
actions: { type: Array, default: () => [] },
data: { type: Object, default: () => {} },
});
const onPermission = (row) => {
if (row.auth === undefined) return true;
return typeof row.auth === 'function' ? row.auth(props.data) : row.auth;
};
</script>
<style lang="scss" scoped>
.custom-table-operate {
&__more {
padding: 20px 5px;
font-size: 20px;
color: $color-primary;
cursor: pointer;
}
.el-button {
&,
&:hover &.is-text:hover,
&.is-text:not(.is-disabled):hover {
background: none !important;
}
}
}
</style>

View File

@ -1,5 +1,6 @@
import SvgIcon from './svg-icon';
import CustomTableOperate from './custom-table-operate';
import CustomRichEditor from './custom-rich-editor';
import CustomEchartBar from './custom-echart-bar';
export { SvgIcon, CustomEchartBar, CustomRichEditor };
export { SvgIcon, CustomTableOperate, CustomEchartBar, CustomRichEditor };

View File

@ -1,9 +1,9 @@
import { ref, computed, unref } from 'vue';
import { useEventListener } from './useEventListener';
let globalScreenRef;
let globalWidthRef;
let globalRealWidthRef;
let globalScreenRef = 0;
let globalWidthRef = 0;
let globalRealWidthRef = 0;
const screenMap = new Map();
screenMap.set('XS', 480);

View File

@ -12,7 +12,7 @@ export const useEcharts = (elRef, theme = 'default') => {
let chartInstance = null;
let resizeFn = resize;
const cacheOptions = ref({});
let removeResizeFn = () => {};
let removeResizeFn = null;
resizeFn = useDebounceFn(resize, 200);

View File

@ -12,7 +12,7 @@ export const useWrapComponents = (Component, route) => {
wrapper = {
name: wrapperName,
render() {
return h('div', { className: 'layout-main' }, Component);
return h('div', { className: 'layout' }, Component);
},
};
wrapperMap.set(wrapperName, wrapper);

View File

@ -1,44 +0,0 @@
<!--
* @Description:
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-27 09:29:36
-->
<template>
<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
</template>
<script setup name="app-link">
import { computed } from 'vue';
import { isExternal } from '@/utils/validate';
const props = defineProps({
to: {
type: String,
required: true,
},
});
const type = computed(() => {
if (isExternal(props.to)) {
return 'a';
}
return 'router-link';
});
const linkProps = (to) => {
if (isExternal(to)) {
return {
href: to,
target: '_blank',
rel: 'noopener',
};
}
return {
to: to,
};
};
</script>

View File

@ -0,0 +1,223 @@
<!--
* @Description:
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-01-25 09:24:27
-->
<template>
<div class="layout">
<div class="layout-header">
<div class="layout-header-top">
<div class="layout-header-top-left">
<span>您好欢迎来到农业产业服务平台</span>
<el-tag>运营大屏</el-tag>
<el-tag>产业服务APP</el-tag>
<el-tag>产业运营管理</el-tag>
</div>
<div class="layout-header-top-right">
<span>商家中心</span>
<span>个人中心</span>
<span>返回首页</span>
</div>
</div>
<div class="layout-header-bottom">
<div class="layout-header-bottom-left">
<div class="layout-header-bottom-search">
<div class="title">农业产业服务平台</div>
<el-input v-model="keyword" placeholder="请输入关键词进行搜索"></el-input>
<el-button type="primary" @click="Search">搜索</el-button>
</div>
<el-menu ellipsis class="layout-header-bottom-menu" mode="horizontal">
<app-link v-for="(item, index) in meuns" :key="index" :to="item.path">
<el-menu-item>{{ item.label }}</el-menu-item>
</app-link>
</el-menu>
</div>
<div class="layout-header-bottom-right">
<div class="layout-header-bottom-right-qr">
<div class="layout-header-bottom-right-qr-img">
<img :src="qrImg" alt="" />
<p>下载数农App</p>
</div>
<div class="layout-header-bottom-right-qr-img">
<img :src="qrImg" alt="" />
<p>打开数农小程序</p>
</div>
</div>
<p>使用数农手机服务更方便</p>
</div>
</div>
</div>
<!-- 子应用不能使用router-view的插槽功能 -->
<router-view v-if="isSubApp" />
<router-view v-else v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive v-if="isReload" :include="cacheRoutes">
<component :is="useWrapComponents(Component, route)" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</div>
</template>
<script setup name="layout">
import { computed, ref } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
import { usePermissionStore } from '@/store/modules/permission';
import { useWrapComponents } from '@/hooks/useWrapComponents';
import { qrImg } from './base64img';
import AppLink from './Link.vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const SettingStore = useSettingStore();
const PermissionStore = usePermissionStore();
const cacheRoutes = computed(() => PermissionStore.keepAliveRoutes);
const isReload = computed(() => SettingStore.isReload);
const isSubApp = computed(() => route.path.includes('sub'));
const keyword = ref('');
const meuns = ref([
{
label: '智慧种植',
path: '/login',
},
{
label: '农事服务',
path: '/sub-admin/home',
},
{
label: '涉农金融',
},
{
label: '电商交易',
},
{
label: '分拣包装',
},
{
label: '仓储物流',
},
{
label: '公共品牌运营',
},
]);
function Search() {
console.log(keyword.value, 'search');
}
</script>
<style lang="scss" scoped>
.layout {
width: 100%;
height: 100%;
box-sizing: border-box;
&-header {
height: 206px;
overflow: hidden;
&-top {
display: flex;
height: 44px;
justify-content: space-between;
align-items: center;
background-color: #f8f8f8;
padding: 0 32px;
&-left {
line-height: 36px;
font-size: 16px;
.el-tag {
color: #fff;
background-color: #a4adb3;
margin-left: 12px;
border-color: #a4adb3;
cursor: pointer;
font-size: 18px;
}
}
&-right {
span {
color: #000;
margin-left: 25px;
line-height: 36px;
font-size: 16px;
text-align: center;
cursor: pointer;
&:nth-child(1) {
margin-left: 0;
font-weight: bold;
}
}
}
}
&-bottom {
display: flex;
padding: 20px 32px 0;
&-left {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(100% - 300px);
.layout-header-bottom-search {
display: flex;
align-items: center;
.title {
font-size: 45px;
white-space: nowrap;
}
.el-input {
flex-grow: 1;
max-width: 460px;
margin-left: 40px;
height: 50px;
font-size: 18px;
}
.el-button {
margin-left: 40px;
font-size: 18px;
height: 45px;
padding: 8px 24px;
}
}
.layout-header-bottom-menu {
justify-content: space-around;
border: none;
.el-menu-item {
font-size: 24px;
}
}
}
&-right {
margin-top: -10px;
width: 240px;
padding-left: 20px;
p {
text-align: center;
}
&-qr {
display: flex;
justify-content: space-between;
&-img {
img {
width: 110px;
height: 110px;
}
p {
text-align: center;
}
}
}
}
}
}
&-main {
width: 100%;
height: calc(100% - 206px);
background-color: #f8f8f8;
}
}
</style>

View File

@ -2,113 +2,29 @@
* @Description:
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang 1048523306@qq.com
* @LastEditTime: 2025-01-17 16:36:25
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-01-25 09:28:53
-->
<template>
<div class="layout">
<div class="layout-header">
<div class="layout-header-top">
<div class="layout-header-top-left">
<span>您好欢迎来到农业产业服务平台</span>
<el-tag>运营大屏</el-tag>
<el-tag>产业服务APP</el-tag>
<el-tag>产业运营管理</el-tag>
</div>
<div class="layout-header-top-right">
<span>商家中心</span>
<span>个人中心</span>
<span>返回首页</span>
</div>
</div>
<div class="layout-header-bottom">
<div class="layout-header-bottom-left">
<div class="layout-header-bottom-search">
<div class="title">农业产业服务平台</div>
<el-input v-model="keyword" placeholder="请输入关键词进行搜索"></el-input>
<el-button type="primary" @click="Search">搜索</el-button>
</div>
<el-menu ellipsis class="layout-header-bottom-menu" mode="horizontal">
<app-link v-for="(item, index) in meuns" :key="index" :to="item.path">
<el-menu-item>{{ item.label }}</el-menu-item>
</app-link>
</el-menu>
</div>
<div class="layout-header-bottom-right">
<div class="layout-header-bottom-right-qr">
<div class="layout-header-bottom-right-qr-img">
<img :src="qrImg" alt="" />
<p>下载数农App</p>
</div>
<div class="layout-header-bottom-right-qr-img">
<img :src="qrImg" alt="" />
<p>打开数农小程序</p>
</div>
</div>
<p>使用数农手机服务更方便</p>
</div>
</div>
</div>
<!-- 子应用不能使用router-view的插槽功能 -->
<router-view v-if="isSubApp" />
<router-view v-else v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive v-if="isReload" :include="cacheRoutes">
<component :is="useWrapComponents(Component, route)" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</div>
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive v-if="isReload" :include="cacheRoutes">
<component :is="useWrapComponents(Component, route)" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</template>
<script setup name="layout">
import { computed, ref } from 'vue';
import { computed } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
import { usePermissionStore } from '@/store/modules/permission';
import { useWrapComponents } from '@/hooks/useWrapComponents';
import { qrImg } from './base64img';
import AppLink from './Link.vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const SettingStore = useSettingStore();
const PermissionStore = usePermissionStore();
const cacheRoutes = computed(() => PermissionStore.keepAliveRoutes);
const isReload = computed(() => SettingStore.isReload);
const isSubApp = computed(() => route.path.includes('sub'));
const keyword = ref('');
const meuns = ref([
{
label: '智慧种植',
path: '/login',
},
{
label: '农事服务',
path: '/sub-admin/home',
},
{
label: '涉农金融',
},
{
label: '电商交易',
},
{
label: '分拣包装',
},
{
label: '仓储物流',
},
{
label: '公共品牌运营',
},
]);
function Search() {
console.log(keyword.value, 'search');
}
</script>
<style lang="scss" scoped>
@ -116,108 +32,5 @@ function Search() {
width: 100%;
height: 100%;
box-sizing: border-box;
&-header {
height: 206px;
overflow: hidden;
&-top {
display: flex;
height: 44px;
justify-content: space-between;
align-items: center;
background-color: #f8f8f8;
padding: 0 32px;
&-left {
line-height: 36px;
font-size: 16px;
.el-tag {
color: #fff;
background-color: #a4adb3;
margin-left: 12px;
border-color: #a4adb3;
cursor: pointer;
font-size: 18px;
}
}
&-right {
span {
color: #000;
margin-left: 25px;
line-height: 36px;
font-size: 16px;
text-align: center;
cursor: pointer;
&:nth-child(1) {
margin-left: 0;
font-weight: bold;
}
}
}
}
&-bottom {
display: flex;
padding: 20px 32px 0;
&-left {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(100% - 300px);
.layout-header-bottom-search {
display: flex;
align-items: center;
.title {
font-size: 45px;
white-space: nowrap;
}
.el-input {
flex-grow: 1;
max-width: 460px;
margin-left: 40px;
height: 50px;
font-size: 18px;
}
.el-button {
margin-left: 40px;
font-size: 18px;
height: 45px;
padding: 8px 24px;
}
}
.layout-header-bottom-menu {
justify-content: space-around;
border: none;
.el-menu-item {
font-size: 24px;
}
}
}
&-right {
margin-top: -10px;
width: 240px;
padding-left: 20px;
p {
text-align: center;
}
&-qr {
display: flex;
justify-content: space-between;
&-img {
img {
width: 110px;
height: 110px;
}
p {
text-align: center;
}
}
}
}
}
}
&-main {
width: 100%;
height: calc(100% - 206px);
background-color: #f8f8f8;
}
}
</style>

View File

@ -5,9 +5,11 @@ import router from './router';
import pinia from './store';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import { registerGlobalMicroApps } from './micro';
import { registerElIcons } from './plugins/icon';
import './utils/permission';
const app = createApp(App);
app.use(pinia).use(router).use(ElementPlus).mount('#root');
registerElIcons(app);
registerGlobalMicroApps();

View File

@ -4,9 +4,9 @@ const { VITE_APP_SUB_VUE, VITE_APP_SUB_ADMIN, VITE_APP_SUB_GAS } = import.meta.e
export const leftApps = [
{
name: 'sub-vue',
name: 'sub-operation-service',
entry: VITE_APP_SUB_VUE,
activeRule: '/sub-vue/',
activeRule: '/sub-operation-service/',
title: '运营服务',
icon: 'platform/icon-home.png',
},
@ -69,7 +69,7 @@ export const microApps = [...leftApps, ...rightApps];
const apps = microApps.map((item) => {
return {
...item,
container: '#sub-app',
container: '#app',
props: {
routerBase: item.activeRule,
getGlobalState: actions.getGlobalState,

View File

@ -2,9 +2,7 @@ import { registerMicroApps, setDefaultMountApp, start, addGlobalUncaughtErrorHan
import microApps from './app';
const registerGlobalMicroApps = () => {
// 给子应用配置加上loader方法
const apps = microApps.map((item) => {
// console.log('registerGlobalMicroApps==', item);
return {
...item,
};
@ -12,25 +10,22 @@ const registerGlobalMicroApps = () => {
registerMicroApps(apps, {
beforeLoad: (app) => {
console.log('before load', app);
switch (app.name) {
case 'sub-vue':
document.title = 'sub-vue';
break;
case 'sub-admin':
document.title = 'sub-admin';
break;
}
console.log('before load=', app);
},
beforeMount: [
(app) => {
console.log('before mount', app.name);
console.log('before mount=', app);
},
],
afterUnmount: [
(app) => {
console.log('after unmount=', app);
},
],
});
start({
prefetch: false, // 取消预加载
prefetch: true, // 取消预加载
sandbox: { experimentalStyleIsolation: true },
});

View File

@ -1,4 +1,4 @@
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import { createRouter, createWebHistory } from 'vue-router';
import Layout from '@/layouts/index.vue';
export const constantRoutes = [
@ -12,12 +12,6 @@ export const constantRoutes = [
icon: 'Login',
},
},
{
path: '/platform',
component: () => import('@/views/index.vue'),
name: 'platform',
meta: { title: '平台入口', icon: 'House' },
},
{
path: '/',
name: 'layout',
@ -26,28 +20,15 @@ export const constantRoutes = [
meta: { title: '平台入口', icon: 'House' },
children: [
{
path: '/home',
component: () => import('@/views/home.vue'),
name: 'home',
path: '/platform',
component: () => import('@/views/index.vue'),
name: 'platform',
meta: { title: '平台入口', icon: 'House' },
},
{
path: '/sub-admin/:pathMatch(.*)*',
component: () => import('@/views/subApp.vue'),
name: 'sub-admin',
hidden: true,
meta: { title: '子应用入口', icon: 'House' },
},
],
},
];
export const notFoundRouter = {
path: '/:pathMatch(.*)',
name: 'notFound',
redirect: '/404',
};
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import { constantRoutes, notFoundRouter } from '@/router';
import { constantRoutes } from '@/router';
import { createAsyncRoutes, filterAsyncRoutes, filterKeepAlive } from '@/utils/router';
import { useUserStore } from '@/store/modules/user';
import { getTree } from '@/utils';
@ -34,7 +34,6 @@ export const usePermissionStore = defineStore({
} else {
accessedRoutes = this.asyncRoutes || [];
}
accessedRoutes = accessedRoutes.concat(notFoundRouter);
this.routes = constantRoutes.concat(accessedRoutes);
resolve(accessedRoutes);
});

View File

@ -13,23 +13,13 @@ import { usePermissionStore } from '@/store/modules/permission';
NProgress.configure({ showSpinner: false });
const { VITE_APP_NAME } = import.meta.env;
const { VITE_APP_TITLE } = import.meta.env;
const whiteList = ['/login'];
router.beforeEach(async (to, from, next) => {
// 解决三级菜单页面缓存问题
// if (to.matched && to.matched.length > 1) {
// for (let i = 0; i < to.matched.length; i++) {
// const element = to.matched[i];
// if (element.components.default.name === 'ViewBox') {
// to.matched.splice(i, 1);
// }
// }
// }
NProgress.start();
if (typeof to.meta.title === 'string') {
document.title = VITE_APP_NAME + ' | ' + to.meta.title;
document.title = VITE_APP_TITLE + ' | ' + to.meta.title;
}
const userStore = useUserStore();
@ -46,6 +36,11 @@ router.beforeEach(async (to, from, next) => {
accessRoutes.forEach((item) => router.addRoute(item));
next({ ...to, replace: true });
} else {
// 子应用跳转回主应用时判断#app是否还有渲染的子应用,如若没有则重新渲染主应用
if (from.path.includes('/sub') && !to.path.includes('/sub')) {
window.location.reload();
return;
}
next();
}
} catch (error) {
@ -59,26 +54,8 @@ router.beforeEach(async (to, from, next) => {
next(`/login?redirect=${to.path}`);
}
}
// const PermissionStore = usePermissionStore();
// console.log('===', PermissionStore.routes);
// if (!PermissionStore.routes.length) {
// const accessRoutes = await PermissionStore.getRoutes();
// accessRoutes.forEach((item) => router.addRoute(item));
// next({ ...to, replace: true });
// } else {
// next();
// }
});
router.afterEach((to) => {
// qiankun子应用跳转回主应用时判断#app是否还有渲染的子应用,如若没有则重新渲染主应用
// setTimeout(() => {
// if (to.path === '/') {
// if (window.wocwin_qiankun) {
// window.wocwin_qiankun = null;
// }
// }
// }, 300);
router.afterEach(() => {
NProgress.done();
});

View File

@ -1,7 +0,0 @@
<template>
<div>home</div>
</template>
<script setup></script>
<style></style>

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-26 23:04:14
* @LastEditTime: 2025-01-24 15:13:25
-->
<template>
<el-breadcrumb class="layout-breadcrumb" separator="/">
@ -36,3 +36,9 @@ const handleLink = (item) => {
const matched = computed(() => route.matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false));
</script>
<style lang="scss" scoped>
.layout-breadcrumb {
@include flex-row();
align-items: center;
}
</style>

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-02-19 21:11:20
* @LastEditTime: 2025-01-24 15:12:54
-->
<template>
<div class="layout-header-placeholder" :style="stylePlaceholder()"></div>
@ -21,7 +21,6 @@
<Breadcrumb />
</div>
<div class="layout-header-right">
<Size class="layout-header-tool" />
<ScreenFull class="layout-header-tool" />
<Avatar class="layout-header-tool" />
</div>
@ -36,7 +35,6 @@ import { useSettingStore } from '@/store/modules/setting';
import { setPx } from '@/utils';
import Hamburger from '../Hamburger';
import Breadcrumb from '../Breadcrumb';
import Size from '../Size';
import ScreenFull from '../ScreenFull';
import Avatar from '../Avatar';
import TagsView from '../TagsView';

View File

@ -1,40 +0,0 @@
<!--
* @Description: layout
* @Author: zenghua.wang
* @Date: 2024-01-26 20:13:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-27 15:35:52
-->
<template>
<el-dropdown trigger="hover" @command="setAssemblySize">
<el-icon class="iconfont icon-size"></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in assemblySizeList" :key="item" :disabled="globalComSize === item" :command="item">
{{ assemblySizeListCh[item] }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup name="layout-size">
import { reactive, computed } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
const SettingStore = useSettingStore();
const globalComSize = computed(() => SettingStore.themeConfig.globalComSize);
const assemblySizeListCh = reactive({
default: '默认',
large: '大型',
small: '小型',
});
const assemblySizeList = reactive(['default', 'large', 'small']);
const setAssemblySize = (item) => {
if (globalComSize.value === item) return;
SettingStore.setThemeConfig({ key: 'globalComSize', val: item });
};
</script>

View File

@ -8,3 +8,22 @@ export const CONSTANTS = {
PREFIX: `${VITE_APP_NAME}_`,
PRIMARY: '#409eff',
};
export const CRUD_OPTIONS = {
index: true,
indexLabel: '序号',
indexWidth: 80,
selection: true,
align: 'center',
headerAlign: 'center',
gridBtn: false,
addBtn: true,
viewBtn: false,
editBtn: false,
delBtn: false,
gutter: 20,
labelWidth: 150,
column: [],
menuWidth: 100,
actions: [],
};

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-02-19 21:11:20
* @LastEditTime: 2025-01-24 15:11:22
-->
<template>
<div class="layout-header-placeholder" :style="stylePlaceholder()"></div>
@ -21,7 +21,6 @@
<Breadcrumb />
</div>
<div class="layout-header-right">
<Size class="layout-header-tool" />
<ScreenFull class="layout-header-tool" />
<Avatar class="layout-header-tool" />
</div>
@ -36,7 +35,6 @@ import { useSettingStore } from '@/store/modules/setting';
import { setPx } from '@/utils';
import Hamburger from '../Hamburger';
import Breadcrumb from '../Breadcrumb';
import Size from '../Size';
import ScreenFull from '../ScreenFull';
import Avatar from '../Avatar';
import TagsView from '../TagsView';

View File

@ -1,40 +0,0 @@
<!--
* @Description: layout
* @Author: zenghua.wang
* @Date: 2024-01-26 20:13:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-27 15:35:52
-->
<template>
<el-dropdown trigger="hover" @command="setAssemblySize">
<el-icon class="iconfont icon-size"></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in assemblySizeList" :key="item" :disabled="globalComSize === item" :command="item">
{{ assemblySizeListCh[item] }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup name="layout-size">
import { reactive, computed } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
const SettingStore = useSettingStore();
const globalComSize = computed(() => SettingStore.themeConfig.globalComSize);
const assemblySizeListCh = reactive({
default: '默认',
large: '大型',
small: '小型',
});
const assemblySizeList = reactive(['default', 'large', 'small']);
const setAssemblySize = (item) => {
if (globalComSize.value === item) return;
SettingStore.setThemeConfig({ key: 'globalComSize', val: item });
};
</script>

View File

@ -23,9 +23,9 @@ export default [
meta: { title: '基地档案', icon: 'Document' },
},
{
path: '/planting-land2',
component: () => import('@/views/index.vue'),
name: 'planting-land2',
path: '/planting-seed',
component: () => import('@/views/trace/planting/seed.vue'),
name: 'planting-seed',
meta: { title: '种子档案', icon: 'Document' },
},
],

View File

@ -124,3 +124,16 @@
color: $color-danger;
}
}
// fix
.avue-form {
.el-form-item__label {
display: flex;
flex-direction: row;
align-items: center;
.el-tooltip__trigger {
margin: 0 3px;
}
}
}

View File

@ -1,6 +1,7 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.page"
@ -14,13 +15,18 @@
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
></avue-crud>
>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { sleep } from '@/utils';
import { CRUD_OPTIONS } from '@/config';
import Mock from 'mockjs';
const res = Mock.mock({
@ -39,21 +45,14 @@ const res = Mock.mock({
});
const app = useApp();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: {},
form: {},
options: {
selection: true,
align: 'center',
headerAlign: 'center',
gridBtn: false,
addBtn: true,
viewBtn: true,
editBtn: true,
...CRUD_OPTIONS,
delBtn: false,
gutter: 20,
labelWidth: 150,
column: [
{
label: '土地编号',
@ -132,6 +131,18 @@ const state = reactive({
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
],
},
page: {
total: 0,
@ -162,18 +173,16 @@ const searchChange = (params, done) => {
state.query = params;
state.page.currentPage = 1;
loadData();
app.$message.success('搜索成功');
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
// row
const rowClick = (row, event, column) => {
app.$message.success(JSON.stringify(row));
//
const rowView = (row) => {
crudRef.value.rowView(row);
};
//
@ -182,14 +191,16 @@ const rowSave = (row, done, loading) => {
};
//
const rowEdit = (row) => {
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
// loading.value = true;
console.log('update=', row);
done(row);
};
//
const rowDel = (row, index, done) => {
console.log('del=', row);
done(row);
};
</script>

View File

@ -1,6 +1,7 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.page"
@ -17,13 +18,23 @@
<template #menu-left>
<el-button type="danger" icon="delete" @click="onDel">批量删除</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { reactive, ref, computed } from 'vue';
import { useApp } from '@/hooks';
import { sleep } from '@/utils';
import { CRUD_OPTIONS } from '@/config';
import { useSettingStore } from '@/store/modules/setting';
const SettingStore = useSettingStore();
const globalComSize = computed(() => SettingStore.themeConfig.globalComSize);
import Mock from 'mockjs';
const res = Mock.mock({
@ -45,26 +56,16 @@ const res = Mock.mock({
});
const app = useApp();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: {},
form: {},
selection: [],
options: {
index: true,
indexLabel: '序号',
indexWidth: 80,
selection: true,
align: 'center',
headerAlign: 'center',
gridBtn: false,
addBtn: true,
addBtnText: '添加档案',
viewBtn: true,
editBtn: true,
delBtn: true,
gutter: 20,
labelWidth: 150,
...CRUD_OPTIONS,
addBtnText: '新增档案',
size: globalComSize.value,
column: [
{
label: '基地代码',
@ -169,19 +170,37 @@ const state = reactive({
},
},
{
label: '面积',
label: '面积(亩)',
prop: 'area',
type: 'number',
labelTip: '提示语',
labelTipPlacement: 'bottom',
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '海拔',
label: '海拔(米)',
prop: 'p2',
type: 'number',
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '创建日期',
prop: 'createdTime',
type: 'datetimerange',
searchRange: true,
search: true,
rangeSeparator: '-',
searchSpan: 8,
rangeSeparator: '至',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
width: 200,
@ -189,6 +208,24 @@ const state = reactive({
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
page: {
total: 0,
@ -219,7 +256,6 @@ const searchChange = (params, done) => {
state.query = params;
state.page.currentPage = 1;
loadData();
app.$message.success('搜索成功');
};
//
@ -228,21 +264,29 @@ const refreshChange = () => {
app.$message.success('刷新成功');
};
//
const rowView = (row) => {
crudRef.value.rowView(row);
};
//
const rowSave = (row, done, loading) => {
console.log('add=', row);
};
//
const rowEdit = (row) => {
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
// loading.value = true;
console.log('update=', row);
done(row);
};
//
const rowDel = (row, index, done) => {
app
.$confirm(`您确定要删除?`, '确定删除', {
.$confirm(`删除后信息将不可查看,确认要删除该条信息内容吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',

View File

@ -0,0 +1,213 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.page"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
>
<template #menu-left>
<el-button type="danger" icon="delete" @click="onDel">批量删除</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { sleep } from '@/utils';
import { CRUD_OPTIONS } from '@/config';
import Mock from 'mockjs';
const res = Mock.mock({
'data|20': [
{
id: '@increment(100000)',
name: '@ctitle(5,10)',
shop: '@ctitle(10,30)',
buyTime: '@datetime("yyyy-MM-dd HH:mm:ss")',
avalibleTime: '@datetime("yyyy-MM-dd HH:mm:ss")',
createdTime: '@datetime("yyyy-MM-dd HH:mm:ss")',
},
],
});
const app = useApp();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: {},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '新增档案',
column: [
{
label: '种子代码',
prop: 'id',
fixed: true,
display: false,
},
{
label: '种子名称',
prop: 'name',
fixed: true,
search: true,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '供应商',
prop: 'shop',
search: true,
with: 300,
overHidden: true,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '采购日期',
prop: 'buyTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
width: 200,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '有效日期',
prop: 'avalibleTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
width: 200,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '创建日期',
prop: 'createdTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
width: 200,
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
page: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
});
const loadData = async () => {
state.loading = true;
await sleep(500);
state.data = res.data;
state.page = {
total: 20,
currentPage: 1,
pageSize: 10,
};
state.loading = false;
};
loadData();
//
const searchChange = (params, done) => {
console.log('search==', params);
if (done) done();
state.query = params;
state.page.currentPage = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
};
//
const rowView = (row) => {
crudRef.value.rowView(row);
};
//
const rowSave = (row, done, loading) => {
console.log('add=', row);
};
//
const rowEdit = (row) => {
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
// loading.value = true;
console.log('update=', row);
};
//
const rowDel = (row, index, done) => {
app
.$confirm(`删除后信息将不可查看,确认要删除该条信息内容吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
console.log('del=', row);
})
.catch(() => {});
};
const onDel = () => {};
</script>