文毅 vor 1 Jahr
Ursprung
Commit
c9c1fd58ef
39 geänderte Dateien mit 7149 neuen und 454 gelöschten Zeilen
  1. 2981 0
      h5/pnpm-lock.yaml
  2. BIN
      h5/public/left.png
  3. BIN
      h5/public/right.png
  4. 3 1
      h5/src/api/Urls.ts
  5. 17 0
      h5/src/api/config/index.ts
  6. 4 0
      h5/src/api/enum/io.ts
  7. 50 0
      h5/src/api/model/Admin.ts
  8. 59 0
      h5/src/api/model/Good.ts
  9. 52 0
      h5/src/api/model/GoodClass.ts
  10. 40 0
      h5/src/api/model/OrderInout.ts
  11. 48 0
      h5/src/api/model/Repertory.ts
  12. 10 0
      h5/src/api/model/Role.ts
  13. 40 0
      h5/src/api/model/realinventory.ts
  14. 40 0
      h5/src/api/model/transfer.ts
  15. BIN
      h5/src/assets/bg.png
  16. BIN
      h5/src/assets/ck.png
  17. BIN
      h5/src/assets/cktal.png
  18. BIN
      h5/src/assets/pd.png
  19. BIN
      h5/src/assets/rk.png
  20. 2 4
      h5/src/config.ts
  21. 6 6
      h5/src/i18n/lang/zh-cn.ts
  22. 63 175
      h5/src/router/route.ts
  23. 2 2
      h5/src/stores/themeConfig.ts
  24. 533 0
      h5/src/theme/common/ycCommon.scss
  25. 5 0
      h5/src/theme/common/ycIndex.scss
  26. 198 0
      h5/src/views/admin/admin/import.vue
  27. 327 0
      h5/src/views/admin/admin/index.vue
  28. 295 0
      h5/src/views/admin/roleManage/edit.vue
  29. 255 0
      h5/src/views/admin/roleManage/index.vue
  30. 107 0
      h5/src/views/admin/roleManage/role.ts
  31. 759 0
      h5/src/views/data/good.vue
  32. 431 0
      h5/src/views/data/repertory.vue
  33. 6 0
      h5/src/views/data/status.ts
  34. 7 266
      h5/src/views/home/index.vue
  35. 168 0
      h5/src/views/payrollModule/crkexport.vue
  36. 171 0
      h5/src/views/payrollModule/export.vue
  37. 223 0
      h5/src/views/payrollModule/import.vue
  38. 170 0
      h5/src/views/payrollModule/kcexport.vue
  39. 77 0
      h5/src/views/system/baseSettings/components/Setting.vue

Datei-Diff unterdrückt, da er zu groß ist
+ 2981 - 0
h5/pnpm-lock.yaml


BIN
h5/public/left.png


BIN
h5/public/right.png


+ 3 - 1
h5/src/api/Urls.ts

@@ -29,7 +29,9 @@ const Urls = {
 	},
 	//仓库模型
 	repertory: {
-		list: '/admin/repo/page',
+		list: '/admin/project/page',//分页
+		statuslist:'/admin/project/status/list',//获取状态字典
+
 		add: '/admin/repo/create', //创建仓库
 		edit: '/admin/repo/update', //仓库更新
 		delete: '/admin/repo/delete', //仓库删除

+ 17 - 0
h5/src/api/config/index.ts

@@ -0,0 +1,17 @@
+import request from '/@/utils/request'
+
+export const update = (code: string, name: string, content: object) => {
+	return request.post('/admin/config/update', {
+		code,
+		name,
+		content
+	})
+}
+
+export const content = (code: string) => {
+	return request.get('/admin/config/content', {
+		params: { code }
+	})
+}
+
+export default update

+ 4 - 0
h5/src/api/enum/io.ts

@@ -0,0 +1,4 @@
+export default {
+    IN: 1,
+    OUT: 2
+}

+ 50 - 0
h5/src/api/model/Admin.ts

@@ -0,0 +1,50 @@
+import Http from "/@/utils/net/Http";
+import Urls from "../Urls";
+const Admin = {
+    /**
+     * 初始化
+     * @returns 
+     */
+    async init() {
+        let url = Urls.admin.init;
+        let res = await Http.get(url);
+        return res;
+    },
+    /**
+     * 获取列表
+     * @param params 
+     * @returns 
+     */
+    async list(params: any) {
+        let url = Urls.admin.list;
+        let res = await Http.get(url, params);
+        return res;
+    },
+    async add(params: any) {
+        let url = Urls.admin.add;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async edit(params: any) {
+        let url = Urls.admin.edit;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async delete(ids: string) {
+        let url = Urls.admin.delete;
+        let res = await Http.get(url, { ids });
+        return res;
+    },
+    async detail(id: string | number) {
+        let url = Urls.admin.detail;
+        let res = await Http.get(url, { id });
+        return res;
+    },
+    async resetPwd(id: string | number, password: string) {
+        let url = Urls.admin.resetPwd;
+        let res = await Http.get(url, { id, password });
+        return res;
+    }
+
+};
+export default Admin;

+ 59 - 0
h5/src/api/model/Good.ts

@@ -0,0 +1,59 @@
+import Http from '/@/utils/net/Http';
+import Urls from '../Urls';
+//gzs:物品模型
+const Good = {
+	/**
+	 * 初始化
+	 * @returns
+	 */
+	async init() {
+		let url = Urls.admin.init;
+		let res = await Http.get(url);
+		return res;
+	},
+	/**
+	 * 获取列表
+	 * @param params
+	 * @returns
+	 */
+	async list(params: any, sort = 'valid') {
+		let url = Urls.good.list;
+		let res = await Http.post(url, params);
+		let repos = res.data.data;
+		if (sort == 'valid') {
+			repos?.sort((a: any, b: any) => b.valid - a.valid);
+		}
+		return res;
+	},
+	async add(params: any) {
+		let url = Urls.good.add;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async edit(params: any) {
+		let url = Urls.good.edit;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async delete(id: any) {
+		let url = Urls.good.delete;
+		let res = await Http.post(url, id);
+		return res;
+	},
+	async export(params: any) {
+		let url = Urls.good.export;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async import(params: any) {
+		let url = Urls.good.import;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async no(no: any) {
+		let url = Urls.good.no;
+		let res = await Http.get(url, no);
+		return res;
+	},
+};
+export default Good;

+ 52 - 0
h5/src/api/model/GoodClass.ts

@@ -0,0 +1,52 @@
+import Http from "/@/utils/net/Http";
+import Urls from "../Urls";
+//gzs:物品模型
+const GoodClass = {
+    /**
+     * 初始化
+     * @returns 
+     */
+    async init() {
+        let url = Urls.admin.init;
+        let res = await Http.get(url);
+        return res;
+    },
+    /**
+     * 获取列表
+     * @param params 
+     * @returns 
+     */
+    async list(params: any) {
+        let url = Urls.goodClass.list;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async add(params: any) {
+        let url = Urls.goodClass.add;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async edit(params: any) {
+        let url = Urls.goodClass.edit;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async delete(id: any) {
+        let url = Urls.goodClass.delete;
+        let res = await Http.post(url, id );
+        return res;
+    },
+    async all() {
+        let url = Urls.goodClass.all;
+        let res = await Http.get(url);
+        return res;
+    },
+    // async detail(id: string | number) {
+    //     let url = Urls.goodClass.detail;
+    //     let res = await Http.post(url, { id });
+    //     return res;
+    // },
+
+
+};
+export default GoodClass;

+ 40 - 0
h5/src/api/model/OrderInout.ts

@@ -0,0 +1,40 @@
+import Http from '/@/utils/net/Http';
+import Urls from '../Urls';
+//gzs:出入库\盘点模型
+const OrderInout = {
+	/**
+	 * 初始化
+	 * @returns
+	 */
+	// async init() {
+	// 	let url = Urls.orderInout.init;
+	// 	let res = await Http.get(url);
+	// 	return res;
+	// },
+	async list(params: any) {
+		let url = Urls.orderInout.list;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async add(params: any) {
+		let url = Urls.orderInout.add;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async detail(id: any) {
+		let url = Urls.orderInout.detail;
+		let res = await Http.post(url, id);
+		return res;
+	},
+	async info(good_id: any, repo_id: any) {
+		let url = Urls.orderInout.info;
+		let res = await Http.get(url, { good_id, repo_id });
+		return res;
+	},
+	async reset(id: any) {
+		let url = Urls.orderInout.reset;
+		let res = await Http.post(url, {id});
+		return res;
+	},
+};
+export default OrderInout;

+ 48 - 0
h5/src/api/model/Repertory.ts

@@ -0,0 +1,48 @@
+import Http from '/@/utils/net/Http';
+import Urls from '../Urls';
+import { data } from '/@/views/data/status';
+//gzs:仓库模型
+const Repertory = {
+	/**
+	 * 初始化
+	 * @returns
+	 */
+	async init() {
+		let url = Urls.admin.init;
+		let res = await Http.get(url);
+		return res;
+	},
+	/**
+	 * 获取列表
+	 * @param params
+	 * @returns
+	 */
+	async list(params: any) {
+		let url = Urls.repertory.list;
+		let res = await Http.post(url, params);
+		return res;
+	},
+
+	async add(params: any) {
+		let url = Urls.repertory.add;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async edit(params: any) {
+		let url = Urls.repertory.edit;
+		let res = await Http.post(url, params);
+		return res;
+	},
+	async delete(id: any) {
+		let url = Urls.repertory.delete;
+		let res = await Http.post(url, { id });
+		return res;
+	},
+	async statuslist(params: any) {
+		let url = Urls.repertory.statuslist;
+		let res = await Http.get(url, params);
+		data = res.data;
+		return res;
+	},
+};
+export default Repertory;

+ 10 - 0
h5/src/api/model/Role.ts

@@ -0,0 +1,10 @@
+import Http from "/@/utils/net/Http";
+import Urls from "../Urls";
+const Role = {
+    async getList(params: any) {
+        let url = Urls.role.list;
+        let res = await Http.get(url, params);
+        return res;
+    }
+};
+export default Role;

+ 40 - 0
h5/src/api/model/realinventory.ts

@@ -0,0 +1,40 @@
+import Http from "/@/utils/net/Http";
+import Urls from "../Urls";
+//gzs:在途库存模型
+const realinventory = {
+    /**
+     * 初始化
+     * @returns 
+     */
+    async init() {
+        let url = Urls.admin.init;
+        let res = await Http.get(url);
+        return res;
+    },
+    /**
+     * 获取列表
+     * @param params 
+     * @returns 
+     */
+    async list(params: any) {
+        let url = Urls.realinventory.list;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async export(params: any) {
+        let url = Urls.realinventory.export;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async detail(params: any) {
+        let url = Urls.realinventory.detail;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async edit(params: any) {
+        let url = Urls.realinventory.edit;
+        let res = await Http.post(url, params);
+        return res;
+    },
+};
+export default realinventory;

+ 40 - 0
h5/src/api/model/transfer.ts

@@ -0,0 +1,40 @@
+import Http from "/@/utils/net/Http";
+import Urls from "../Urls";
+//gzs:物品模型
+const Transfer = {
+    /**
+     * 初始化
+     * @returns 
+     */
+    async init() {
+        let url = Urls.admin.init;
+        let res = await Http.get(url);
+        return res;
+    },
+    /**
+     * 获取列表
+     * @param params 
+     * @returns 
+     */
+    async list(params: any) {
+        let url = Urls.transfer.list;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async detail(params: any) {
+        let url = Urls.transfer.detail;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async add(params: any) {
+        let url = Urls.transfer.add;
+        let res = await Http.post(url, params);
+        return res;
+    },
+    async reset(id: any) {
+		let url = Urls.transfer.reset;
+		let res = await Http.post(url, {id});
+		return res;
+	},
+};
+export default Transfer;

BIN
h5/src/assets/bg.png


BIN
h5/src/assets/ck.png


BIN
h5/src/assets/cktal.png


BIN
h5/src/assets/pd.png


BIN
h5/src/assets/rk.png


+ 2 - 4
h5/src/config.ts

@@ -19,10 +19,8 @@ const config = {
 let env = import.meta.env;
 console.log("==env.Mode:==", env.MODE);
 if (env.MODE == "development") {
-    config.file = "http://192.168.0.170:8880";
-    config.host = "http://192.168.0.170:8880/public/index.php";
-    // config.host = "http://localhost:80/index.php";
-    // config.host = "http://local81.lzj/simple_storage/api/public/index.php";
+    config.file = "http://192.168.0.170/project-manager";
+    config.host = "http://192.168.0.170/project-manager/index.php";
 }
 if (env.MODE == 'release') {
 	//本地

+ 6 - 6
h5/src/i18n/lang/zh-cn.ts

@@ -3,19 +3,19 @@
 export default {
 	router: {
 		home: '首页',
-		admin: '账号管理',
-		adminRole: "角色管理",
+		admin: '账户管理',
 		adminMng: "账号管理",
-		underlying: '基础数据',
-		dataRepertory: '仓库管理',
+		adminRole: "角色管理",
+		underlying: '项目管理',
+		dataRepertory: '项目汇总',
+		dataGood: '项目详情',
+		// 以下的会废弃
 		orderInout: "出入库管理",
-		dataGood: '物品管理',
 		dataGoodClass:'物品分类',
 		orderTransfer:'调拨',
 		orderInventory:'盘点',
 		inventoryRealinventory:'在途库存',
 		inventoryIntransit:'实时库存',
-		// 以下的会废弃
 		underlyingRoleManage: '账号权限02',
 		underlyingDepartment: '部门人员03',
 		system: '系统设置',

+ 63 - 175
h5/src/router/route.ts

@@ -84,7 +84,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 						name: 'adminMng',
 						component: () => import('/@/views/admin/admin/index.vue'),
 						meta: {
-							title: 'message.router.adminMng', //管理员管理
+							title: 'message.router.adminMng', //账号管理
 							isLink: '',
 							isHide: false,
 							isKeepAlive: true,
@@ -94,216 +94,104 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 							icon: 'iconfont icon-gerenzhongxin',
 						},
 					},
-				],
-			},
-			{
-				path: '/data',
-				name: 'data',
-				component: () => import('/@/layout/routerView/parent.vue'),
-				redirect: '/data/index',
-				meta: {
-					title: 'message.router.underlying',
-					isLink: '',
-					isHide: false,
-					isKeepAlive: true,
-					isAffix: false,
-					isIframe: false,
-					roles: ['admin'],
-					icon: 'iconfont icon-caidan',
-				},
-				children: [
-					{
-						path: '/data/repertory',
-						name: 'dataRepertory',//gzs:name一定不能重复
-						component: () => import('/@/views/data/repertory.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-							title: 'message.router.dataRepertory', //gzs:左侧菜单栏显示的名称
-							isLink: '',
-							isHide: false,
-							isKeepAlive: true,
-							isAffix: false,
-							isIframe: false,
-							roles: ['admin'],
-							icon: 'iconfont icon-caidan',
-						},
-					},
-					{
-						path: '/data/good',
-						name: 'dataGood',//gzs:name一定不能重复
-						component: () => import('/@/views/data/good.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-							title: 'message.router.dataGood', //gzs:左侧菜单栏显示的名称
-							isLink: '',
-							isHide: false,
-							isKeepAlive: true,
-							isAffix: false, 
-							isIframe: false,
-							roles: ['admin'],
-							icon: 'iconfont icon-caidan',
-						},
-					},
-					{
-						path: '/data/print',
-						name: 'printGood',//gzs:name一定不能重复
-						component: () => import('/@/views/data/print.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-							title: '打印机', //gzs:左侧菜单栏显示的名称
-							isLink: '',
-							isHide: true,
-							isKeepAlive: true,
-							isAffix: false, 
-							isIframe: false,
-							roles: ['admin'],
-							icon: 'iconfont icon-caidan',
-						},
-					},
-					
-				],
-			},
-			{
-				path: '/order',
-				name: 'order',
-				component: () => import('/@/layout/routerView/parent.vue'),
-				redirect: '/data/index',
-				meta: {
-					title: '出入库业务',
-					isLink: '',
-					isHide: false,
-					isKeepAlive: true,
-					isAffix: false,
-					isIframe: false,
-					roles: ['admin'],
-					icon: 'iconfont icon-zhongduancanshu',
-				},
-				children: [
-					{
-						path: '/order/inout',
-						name: 'orderInout',//gzs:name一定不能重复
-						component: () => import('/@/views/order/inout.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-							title: 'message.router.orderInout', //gzs:左侧菜单栏显示的名称
-							isLink: '',
-							isHide: false,
-							isKeepAlive: true,
-							isAffix: false,
-							isIframe: false,
-							roles: ['admin'],
-							icon: 'iconfont icon-zhongduancanshu',
-						},
-					},
 					{
-						path: '/order/transfer',//调拨
-						name: 'orderTransfer',//gzs:name一定不能重复
-						component: () => import('/@/views/order/transfer.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-							title: 'message.router.orderTransfer', //gzs:左侧菜单栏显示的名称
-							isLink: '',
-							isHide: false,
-							isKeepAlive: true,
-							isAffix: false,
-							isIframe: false,
-							roles: ['admin'],
-							icon: 'iconfont icon-zhongduancanshu',
-						},
-					},
-					{
-						path: '/order/inventory',//盘点
-						name: 'orderInventory',//gzs:name一定不能重复
-						component: () => import('/@/views/order/inventory.vue'),//gzs:仓库管理页面文件的路径
+						path: '/admin/adminrole',
+						name: 'adminRole',
+						component: () => import('/@/views/admin/admin/index.vue'),
 						meta: {
-							title: 'message.router.orderInventory', //gzs:左侧菜单栏显示的名称
+							title: 'message.router.adminRole', //角色管理
 							isLink: '',
 							isHide: false,
 							isKeepAlive: true,
 							isAffix: false,
 							isIframe: false,
 							roles: ['admin'],
-							icon: 'iconfont icon-zhongduancanshu',
+							icon: 'iconfont icon-gerenzhongxin',
 						},
 					},
 				],
 			},
 			{
-				path: '/inventory',
-				name: 'inventory',
+				path: '/data',
+				name: 'data',
 				component: () => import('/@/layout/routerView/parent.vue'),
 				redirect: '/data/index',
 				meta: {
-					title: '库存报表',
-					isLink: '',
-					isHide: false,
-					isKeepAlive: true,
-					isAffix: false,
-					isIframe: false,
-					roles: ['admin'],
-					icon: 'iconfont icon-zhongduancanshuchaxun',
-				},
-				children: [
-					{
-						path: '/inventory/realinventory',//在途库存
-						name: 'inventoryRealinventory',//gzs:name一定不能重复
-						component: () => import('/@/views/inventory/realinventory.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-						title: 'message.router.inventoryRealinventory', //gzs:左侧菜单栏显示的名称
-						isLink: '',
-						isHide: false,
-						isKeepAlive: true,
-						isAffix: false,
-						isIframe: false,
-						roles: ['admin'],
-						icon: 'iconfont icon-zhongduancanshuchaxun',
-						},
-					},
-					{
-						path: '/inventory/intransit',//实时库存
-						name: 'inventoryIntransit',//gzs:name一定不能重复
-						component: () => import('/@/views/inventory/intransit.vue'),//gzs:仓库管理页面文件的路径
-						meta: {
-						title: 'message.router.inventoryIntransit', //gzs:左侧菜单栏显示的名称
-						isLink: '',
-						isHide: false,
-						isKeepAlive: true,
-						isAffix: false,
-						isIframe: false,
-						roles: ['admin'],
-						icon: 'iconfont icon-zhongduancanshuchaxun',
-						},
-					},
-				],
-			},
-			{
-				path: '/system',
-				name: 'system',
-				component: () => import('/@/layout/routerView/parent.vue'),
-				redirect: '/system/baseSettings',
-				meta: {
-					title: 'message.router.system',
+					title: 'message.router.underlying',
 					isLink: '',
 					isHide: false,
 					isKeepAlive: true,
 					isAffix: false,
 					isIframe: false,
 					roles: ['admin'],
-					icon: 'iconfont icon-xitongshezhi',
+					icon: 'iconfont icon-caidan',
 				},
 				children: [
 					{
-						path: '/system/baseSettings',
-						name: 'systemBaseSettings',
-						component: () => import('/@/views/system/baseSettings/index.vue'),
+						path: '/data/repertory',
+						name: 'dataRepertory',//gzs:name一定不能重复
+						component: () => import('/@/views/data/repertory.vue'),//gzs:仓库管理页面文件的路径
 						meta: {
-							title: 'message.router.systemBaseSettings',
+							title: 'message.router.dataRepertory', //gzs:左侧菜单栏显示的名称
 							isLink: '',
 							isHide: false,
 							isKeepAlive: true,
 							isAffix: false,
 							isIframe: false,
 							roles: ['admin'],
-							icon: 'iconfont icon-xitongshezhi',
+							icon: 'iconfont icon-caidan',
 						},
 					},
+					// {
+					// 	path: '/data/good',
+					// 	name: 'dataGood',//gzs:name一定不能重复
+					// 	component: () => import('/@/views/data/good.vue'),//gzs:仓库管理页面文件的路径
+					// 	meta: {
+					// 		title: 'message.router.dataGood', //gzs:左侧菜单栏显示的名称
+					// 		isLink: '',
+					// 		isHide: false,
+					// 		isKeepAlive: true,
+					// 		isAffix: false, 
+					// 		isIframe: false,
+					// 		roles: ['admin'],
+					// 		icon: 'iconfont icon-caidan',
+					// 	},
+					// },
 				],
 			},
+			// {
+			// 	path: '/system',
+			// 	name: 'system',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/system/baseSettings',
+			// 	meta: {
+			// 		title: 'message.router.system',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-xitongshezhi',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/system/baseSettings',
+			// 			name: 'systemBaseSettings',
+			// 			component: () => import('/@/views/system/baseSettings/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemBaseSettings',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-xitongshezhi',
+			// 			},
+			// 		},
+			// 	],
+			// },
 		],
 	},
 ];

+ 2 - 2
h5/src/stores/themeConfig.ts

@@ -138,9 +138,9 @@ export const useThemeConfig = defineStore('themeConfig', {
 			 * 全局网站标题 / 副标题
 			 */
 			// 网站主标题(菜单导航、浏览器当前网页标题)
-			globalTitle: '简易仓储管理系统',
+			globalTitle: '项目管理系统',
 			// 网站副标题(登录页顶部文字)
-			globalViceTitle: '简易仓储管理系统',
+			globalViceTitle: '项目管理系统',
 			// 网站副标题(登录页顶部文字)(专注、免费、开源、维护、解疑)
 			globalViceTitleMsg: 'v1.0.1',
 			// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn

+ 533 - 0
h5/src/theme/common/ycCommon.scss

@@ -0,0 +1,533 @@
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+// 主色
+$color-primary: #409EFF;
+
+// 辅助色
+$color-info: #909399;
+$color-success: #67C23A;
+$color-warning: #E6A23C;
+$color-danger: #F56C6C;
+
+// 文字
+$color-text-main: #303133;
+$color-text-normal: #606266;
+$color-text-sub: #909399;
+$color-text-placehoder: #C0C4CC;
+
+// 边框
+$color-border-1: #DCDFE6;
+$color-border-2: #E4E7ED;
+$color-border-3: #EBEEF5;
+$color-border-4: #F2F6FC;
+
+// 背景
+$color-bg: #f8f8f9;
+
+
+//文字颜色
+.text_primary {
+	color: $color-primary;
+}
+
+.text_success {
+	color: $color-success;
+}
+
+.text_warning {
+	color: $color-warning;
+}
+
+.text_info {
+	color: $color-info;
+}
+
+.text_danger {
+	color: $color-danger;
+}
+
+.text_main {
+	color: color-text-main;
+}
+
+.text_normal {
+	color: $color-text-normal;
+}
+
+.text_sub {
+	color: $color-text-sub;
+}
+
+.text_placehoder {
+	color: $color-text-placehoder;
+}
+
+.white {
+	color: #fff !important;
+}
+
+.red {
+	color: #ff0000 !important;
+}
+
+.black {
+	color: #000 !important;
+}
+
+.grey {
+	color: grey !important;
+}
+
+.blue {
+	color: blue !important;
+}
+
+.light-blue {
+	color: #009ceb !important;
+}
+
+//END 文字颜色
+
+.blank_s {
+	height: 8px;
+}
+
+.blank_m {
+	height: 16px;
+}
+
+.blank_l {
+	height: 26px;
+}
+
+.tc {
+	text-align: center;
+}
+
+.tl {
+	text-align: left;
+}
+
+@for $i from 1 through 500 {
+	.el-table#{$i} {
+		width: 100% !important;
+		height: calc(100vh - #{$i}px) !important;
+	}
+}
+
+@for $i from 3 through 14 {
+	.h#{$i*50} {
+		height: #{$i * 50}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.mb#{$i*5} {
+		margin-bottom: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.mt#{$i*5} {
+		margin-top: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.ml#{$i*5} {
+		margin-left: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.mr#{$i*5} {
+		margin-right: #{$i * 5}px !important;
+	}
+}
+
+.m0 {
+	margin: 0 !important;
+}
+
+.ma0 {
+	margin: 0 auto;
+}
+
+.maf1 {
+	margin-left: -1px;
+}
+
+.pb0 {
+	padding-bottom: 0 !important;
+}
+
+@for $i from 0 through 12 {
+	.p#{$i*5} {
+		padding: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.pl#{$i*5} {
+		padding-left: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.pr#{$i*5} {
+		padding-right: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.pb#{$i*5} {
+		padding-bottom: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 0 through 12 {
+	.pt#{$i*5} {
+		padding-top: #{$i * 5}px !important;
+	}
+}
+
+@for $i from 1 through 100 {
+	.pct#{$i} {
+		width: $i * 1%;
+	}
+}
+
+.nowrap {
+	overflow: hidden;
+	-o-text-overflow: ellipsis;
+	/*兼容opera*/
+	text-overflow: ellipsis;
+	/*这就是省略号喽*/
+	overflow: hidden;
+	/*设置超过的隐藏*/
+	white-space: nowrap;
+	/*设置不折行*/
+}
+
+.pos_r {
+	position: relative;
+}
+
+.pos_a {
+	position: absolute;
+}
+
+.pos_f {
+	position: fixed;
+}
+
+.text_l {
+	text-align: left !important;
+}
+
+.text_r {
+	text-align: right !important;
+}
+
+.text_c {
+	text-align: center !important;
+}
+
+
+.tr {
+	text-align: right;
+}
+
+//边框
+//全边框
+.bds_1 {
+	border: solid 1px $color-border-1 !important;
+}
+
+.bds_2 {
+	border: solid 1px $color-border-2 !important;
+}
+
+.bds_3 {
+	border: solid 1px $color-border-3 !important;
+}
+
+.bds_4 {
+	border: solid 1px $color-border-4 !important;
+}
+
+.bdd_1 {
+	border: dashed 1px $color-border-1 !important;
+}
+
+.bdd_2 {
+	border: dashed 1px $color-border-2 !important;
+}
+
+.bdd_3 {
+	border: dashed 1px $color-border-3 !important;
+}
+
+.bdd_4 {
+	border: dashed 1px $color-border-4 !important;
+}
+
+//上边框
+.bdd_t_1 {
+	border-top: dashed 1px $color-border-1 !important;
+}
+
+.bdd_t_2 {
+	border-top: dashed 1px $color-border-2 !important;
+}
+
+.bdd_t_3 {
+	border-top: dashed 1px $color-border-3 !important;
+}
+
+.bdd_t_4 {
+	border-top: dashed 1px $color-border-4 !important;
+}
+
+.bds_t_1 {
+	border-top: solid 1px $color-border-1 !important;
+}
+
+.bds_t_2 {
+	border-top: solid 1px $color-border-2 !important;
+}
+
+.bds_t_3 {
+	border-top: solid 1px $color-border-3 !important;
+}
+
+.bds_t_4 {
+	border-top: solid 1px $color-border-4 !important;
+}
+
+//左边框
+.bdd_l_1 {
+	border-left: dashed 1px $color-border-1 !important;
+}
+
+.bdd_l_2 {
+	border-left: dashed 1px $color-border-2 !important;
+}
+
+.bdd_l_3 {
+	border-left: dashed 1px $color-border-3 !important;
+}
+
+.bdd_l_4 {
+	border-left: dashed 1px $color-border-4 !important;
+}
+
+.bds_l_1 {
+	border-left: solid 1px $color-border-1 !important;
+}
+
+.bds_l_2 {
+	border-left: solid 1px $color-border-2 !important;
+}
+
+.bds_l_3 {
+	border-left: solid 1px $color-border-3 !important;
+}
+
+.bds_l_4 {
+	border-left: solid 1px $color-border-4 !important;
+}
+
+//右边框
+.bdd_r_1 {
+	border-right: dashed 1px $color-border-1 !important;
+}
+
+.bdd_r_2 {
+	border-right: dashed 1px $color-border-2 !important;
+}
+
+.bdd_r_3 {
+	border-right: dashed 1px $color-border-3 !important;
+}
+
+.bdd_r_4 {
+	border-right: dashed 1px $color-border-4 !important;
+}
+
+.bds_r_1 {
+	border-right: solid 1px $color-border-1 !important;
+}
+
+.bds_r_2 {
+	border-right: solid 1px $color-border-2 !important;
+}
+
+.bds_r_3 {
+	border-right: solid 1px $color-border-3 !important;
+}
+
+.bds_r_4 {
+	border-right: solid 1px $color-border-4 !important;
+}
+
+//下边框
+.bds_b_1 {
+	border-bottom: solid 1px $color-border-1 !important;
+}
+
+.bds_b_2 {
+	border-bottom: solid 1px $color-border-2 !important;
+}
+
+.bds_b_3 {
+	border-bottom: solid 1px $color-border-3 !important;
+}
+
+.bds_b_4 {
+	border-bottom: solid 1px $color-border-4 !important;
+}
+
+.bdd_b_1 {
+	border-bottom: dashed 1px $color-border-1 !important;
+}
+
+.bdd_b_2 {
+	border-bottom: dashed 1px $color-border-2 !important;
+}
+
+.bdd_b_3 {
+	border-bottom: dashed 1px $color-border-3 !important;
+}
+
+.bdd_b_4 {
+	border-bottom: dashed 1px $color-border-4 !important;
+}
+
+.bdn {
+	border: none !important;
+}
+
+.bdn_b {
+	border-bottom: none !important;
+}
+
+.bdn_t {
+	border-top: none !important;
+}
+
+.bdn_l {
+	border-left: none !important;
+}
+
+//end 边框
+.bg_white {
+	background: #fff !important;
+}
+
+.bg_f5 {
+	background: #f5f5f5 !important;
+}
+
+.bg_ff {
+	background: #fff !important;
+}
+
+.bg_none {
+	background: none !important;
+}
+
+.cursor {
+	//不要使用,即将废弃
+	cursor: pointer;
+}
+
+.cursor_pointer {
+	cursor: pointer;
+}
+
+.cursor_none {
+	cursor: default !important;
+}
+
+.dis_blk {
+	display: block !important;
+}
+
+.dis_none {
+	display: none !important;
+}
+
+.dis_inline {
+	display: inline !important;
+}
+
+.dis_inb {
+	display: inline-block !important;
+}
+
+.clearfix {
+	clear: both;
+}
+
+.clearfix:after {
+	content: 'clearfix';
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+.clear {
+	clear: both;
+}
+
+.fl {
+	float: left !important;
+}
+
+.fwb {
+	font-weight: bold;
+}
+
+.bgc_grey {
+	background: #ccc;
+}
+
+@for $i from 12 through 50 {
+	.fs#{$i} {
+		font-size: $i * 1px !important;
+	}
+}
+
+.fr {
+	float: right !important;
+}
+
+//border-radius
+@for $i from 10 through 30 {
+	.lh_#{$i*1} {
+		line-height: calc($i /10);
+	}
+}
+
+@for $i from 1 through 10 {
+	.br#{$i*10} {
+		border-radius: #{$i * 1}px !important;
+	}
+}
+
+@for $i from 1 through 10 {
+	.height#{$i*10} {
+		height: #{$i * 10}px !important;
+	}
+}
+
+@for $i from 0 through 90 {
+	.width#{$i*20} {
+		width: #{$i * 20}px !important;
+	}
+}
+
+.opacity0 {
+	opacity: 0;
+}

+ 5 - 0
h5/src/theme/common/ycIndex.scss

@@ -0,0 +1,5 @@
+.yc_pagebox {
+    display: flex;
+    justify-content: flex-end;
+    padding: 20px;
+}

+ 198 - 0
h5/src/views/admin/admin/import.vue

@@ -0,0 +1,198 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="800px" draggable>
+			<div>第一步:请点击下面的链接下载Excel模板,并按照模板填写信息。</div>
+			<el-button class="download-file" key="primary" type="default">
+				<a :href="state.download_file_url" target="target" download>下载模板</a>
+			</el-button>
+			<div class="two">第二步:导入Excel模板文件。</div>
+			<el-form :model="state.ruleForm" ref="refForm">
+				<el-form-item label="文件" prop="path" :rules="[{ required: true, message: '请上传文件' }]">
+					<el-upload
+						ref="uploadEle"
+						class="upload-demo"
+						drag
+						:action="state.uploadUrl"
+						:limit="1"
+						method="post"
+						:on-success="handleAvatarSuccess"
+						:on-remove="ClearFiles"
+					>
+						<el-icon class="el-icon--upload">
+							<upload-filled />
+						</el-icon>
+						<div class="el-upload__text">拖拽文件或者 <em>点击上传</em></div>
+						<template #tip>
+							<div class="el-upload__tip" v-if="state.isTips">已上传文件到:{{ state.fileUrl }},请点击导入按钮开始导入</div>
+						</template>
+					</el-upload>
+				</el-form-item>
+			</el-form>
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">取 消</el-button>
+					<el-button type="primary" @click="onSubmit(refForm)">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="payrollModuleAttendanceImport">
+import { reactive, ref } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import type { ElForm, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { UploadFilled } from '@element-plus/icons-vue';
+type FormInstance = InstanceType<typeof ElForm>;
+import Department from '/@/api/department/department.ts';
+import config from '/@/config.ts';
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const deptDialogFormRef = ref();
+
+const upload = ref<UploadInstance>();
+const refForm = ref<FormInstance>();
+const uploadEle = ref<any>(null);
+const state = reactive<any>({
+	productOptions: [],
+	ruleForm: {
+		path: '',
+	},
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	isTips: false,
+	fileUrl: '',
+	uploadUrl: config.host + '/admin/upload/file',
+	download_file_url: config.file + '/static/admin.xlsx',
+});
+
+// 打开弹窗
+const openDialog = () => {
+	state.ruleForm = {
+		path: '',
+	};
+	ClearFiles();
+	state.dialog.isShowDialog = true;
+    state.dialog.title = '导入员工035';
+    state.dialog.submitTxt = '导 入';
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+
+// 清除文件缓存
+const ClearFiles = () => {
+	console.log('uploadEle.value=>', uploadEle.value);
+	if (uploadEle.value != null) {
+		state.isTips = false;
+		state.ruleForm.path = '';
+		uploadEle.value.clearFiles();
+	}
+};
+
+// 新增
+const onSubmit = (formEl: FormInstance | undefined) => {
+	console.log('state.ruleForm.path', state.ruleForm.path);
+	console.log('onSubmit formEl', formEl);
+	if (!state.ruleForm.path) {
+		ElMessage.error('请先上传');
+		return;
+	}
+	if (!formEl) return;
+	formEl.validate(async (valid) => {
+		console.log('valid', valid);
+		if (valid) {
+			let res: any = null;
+			res = await Department.import(state.ruleForm);
+			if (res.code != 0) {
+				closeDialog();
+				ElMessage.error(res.msg);
+				return;
+			}
+			state.isTips = false;
+			state.ruleForm.path = '';
+			uploadEle.value.clearFiles();
+			ElMessage.success(res.msg);
+			console.log('submit succ!');
+			closeDialog();
+			emit('refresh');
+		} else {
+			ElMessage.error('导入失败,请检查表格是否正确');
+			console.log('error submit!');
+			return false;
+		}
+	});
+};
+
+// 上传文件成功的事件
+const handleAvatarSuccess = (res: any, file: any) => {
+	console.log('res=>', res);
+	console.log('file=>', file);
+	state.ruleForm.path = res.data.file;
+	state.fileUrl = res.data.url;
+	state.isTips = true;
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+.tips {
+	font-size: 12px;
+	line-height: 1.2;
+	color: #666;
+}
+
+.two {
+	margin: 8px 0px;
+}
+
+.download-file {
+	margin-top: 10px;
+	a {
+		text-decoration: none;
+		color: #409eff;
+	}
+}
+
+.avatar-uploader .avatar {
+	width: 178px;
+	height: 178px;
+	display: block;
+}
+
+.avatar-uploader .el-upload {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	cursor: pointer;
+	position: relative;
+	overflow: hidden;
+	transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+	border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+	font-size: 28px;
+	color: #8c939d;
+	width: 178px;
+	height: 178px;
+	text-align: center;
+}
+</style>

+ 327 - 0
h5/src/views/admin/admin/index.vue

@@ -0,0 +1,327 @@
+<template>
+	<div class="p20">
+		<el-card class="box-card">
+			<el-row>
+				<el-col :span="12">
+					<el-text class="mx-1">关键词:</el-text>
+
+					<el-input class="width160" placeholder="请输入关键词" v-model="data.params.keyword"
+						@keyup.enter.native="getList"> </el-input>
+
+					<el-button type="primary" class="ml10" @click="getList">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-col>
+				<el-col :span="12" class="tr">
+					<el-button type="primary" @click="addPage">新增</el-button>
+
+					<el-button type="danger" @click="deleteMult">删除</el-button>
+				</el-col>
+			</el-row>
+		</el-card>
+
+		<el-card class="box-card mt20" v-loading="data.loading">
+			<el-table :data="data.tableData" @selection-change="handleTableDataSelectionChange">
+				<el-table-column type="selection" width="55" />
+				<el-table-column fixed prop="id" label="ID" width="60" />
+				<el-table-column fixed prop="name" label="账号" width="150" />
+				<el-table-column prop="phone" label="手机号" width="" />
+				<el-table-column prop="login_count" label="登录次数" width="120" />
+				<el-table-column prop="login_last_time" label="最后登录时间" min-width="180" />
+				<el-table-column prop="valid" label="状态" width="">
+					<template #default="scope">
+						<el-tag v-if="scope.row.valid == 1">启用</el-tag>
+						<el-tag type="danger" v-if="scope.row.valid == 0">禁用</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="role.name" label="角色" width="120" />
+				<el-table-column fixed="right" label="操作" width="200">
+					<template #default="scope">
+						<el-button link type="primary" @click="resetPwd(scope.row)">重置密码</el-button>
+						<el-button link type="primary" @click="edit(scope.row)">编辑</el-button>
+						<el-button link type="danger" @click="deleteOne(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<div class="yc_pagebox">
+				<el-pagination v-model:current-page="data.pagination.currentPage"
+					v-model:page-size="data.pagination.pageSize" :page-sizes="getThemeConfig.pageSizeArray"
+					layout="total, sizes, prev, pager, next, jumper" :total="data.pagination.total"
+					@size-change="handleSizeChange" @current-change="handleCurrentChange" />
+			</div>
+		</el-card>
+
+		<!-- 弹出容器 -->
+		<vuecmf-dialog width="500px" :model_value="data.dialog.show" :title="data.dialog.title" @close="closePage">
+			<template #content>
+				<el-form :model="data.form" label-width="120px" ref="formRef" v-loading="data.dialog.isLoading">
+					<el-form-item label="账号" prop="name" :rules="[{ required: true, message: '请填写账号', trigger: 'blur' }]">
+						<el-input v-model="data.form.name" placeholder="账号提交后不能修改" :disabled="data.form.id > 0" />
+					</el-form-item>
+					<el-form-item label="手机号" prop="phone" :rules="[
+						{ required: true, message: '请填写手机号', trigger: 'blur' },
+						{ validator: ruleReg.phoneReg(data.form.phone), message: '请填写正确的手机号', trigger: 'blur' },
+					]">
+						<el-input v-model="data.form.phone" placeholder="填写手机号码" />
+					</el-form-item>
+					<el-form-item label="登录密码" prop="password"
+						:rules="[{ required: true, message: '请填写登录密码', trigger: 'blur' }]" v-if="data.form.id == 0">
+						<el-input v-model="data.form.password" type="password" placeholder="建议密码包含字母和数字" show-password />
+					</el-form-item>
+					<el-form-item label="角色" prop="role_id"
+						:rules="[{ required: true, message: '请选择角色', trigger: 'blur' }]">
+						<el-select v-model="data.form.role_id" placeholder="请选择角色">
+							<el-option v-for="(item, index) in data.roleList" :key="item.id" :label="item.name"
+								:value="item.id" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="状态" prop="valid" :rules="[{ required: true, message: '请选择状态', trigger: 'blur' }]">
+						<el-radio-group v-model="data.form.valid">
+							<el-radio :label="1">启用</el-radio>
+							<el-radio :label="0">禁用</el-radio>
+						</el-radio-group>
+					</el-form-item>
+				</el-form>
+				<div class="height20"></div>
+			</template>
+			<template #footer>
+				<el-button type="default" @click="closePage">取消</el-button>
+				<el-button type="primary" @click="submitForm(formRef)">提交</el-button>
+			</template>
+		</vuecmf-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { defineAsyncComponent, onMounted, reactive, computed, ref, watch } from 'vue';
+
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+import Admin from '/@/api/model/Admin';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { FormInstance, FormRules } from 'element-plus';
+import ruleReg from '/@/utils/ruleReg';
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+const formRef = ref<FormInstance>();
+const multipleSelection = ref([]);
+let data = reactive({
+	params: {
+		pageSize: getThemeConfig.value.pageSize,
+		page: 1,
+		keyword: '',
+	},
+	pagination: {
+		currentPage: 1,
+		pageSize: getThemeConfig.value.pageSize,
+		total: 100,
+	},
+	loading: false, //是否加载中
+	tableData: [], // 表格数据
+	dialog: {
+		show: false, // 是否显示弹出框,用于最大化、最小化及还原
+		title: '添加', // 弹出框标题
+		isLoading: false, //是否加载中
+	},
+	roleList: [] as any[], //角色列表
+	form: {} as any,
+});
+
+const newForm = () => {
+	let form = {
+		id: 0,
+		name: '',
+		password: '',
+		phone: '',
+		role_id: '',
+		valid: 1,
+	};
+	return form;
+};
+/**
+ * 监听页数
+ */
+watch(() => data.params.keyword, () => {
+	data.params.page = 1
+}
+)
+/**
+ * 切换页面大小
+ * @param val
+ */
+const handleSizeChange = (val: number) => {
+	data.params.pageSize = val;
+	getList();
+};
+/**
+ * 翻页
+ */
+const handleCurrentChange = (val: number) => {
+	data.params.page = val;
+	getList();
+};
+
+const search = async () => {
+	data.params.page = 1;
+	await getList();
+};
+const init = async () => {
+	let res = await Admin.init();
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	data.roleList = res.data.roleList;
+	console.log('lzj500🚀 ~ file: index.vue:170 ~ init ~ roleList:', data.roleList);
+};
+const getList = async () => {
+	data.loading = true;
+	const res = await Admin.list(data.params);
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	data.tableData = res.data.data;
+	//分页数据赋值
+	data.pagination.total = res.data.total;
+	data.pagination.currentPage = res.data.current_page;
+	data.pagination.pageSize = res.data.per_page;
+	console.log('lzj500🚀 ~ file: index.vue:95 ~ getList ~ res:', res);
+};
+const handleTableDataSelectionChange = (val: any) => {
+	multipleSelection.value = val;
+	console.log('lzj500🚀 ~ file: index.vue:203 ~ handleTableDataSelectionChange ~ val:', val);
+};
+//=========添加 和 编辑 删除
+
+const closePage = () => {
+	data.dialog.show = false;
+};
+
+const addPage = () => {
+	data.dialog.show = true;
+	data.dialog.title = '添加管理员';
+	data.form = newForm();
+	console.log('lzj500🚀 ~ file: index.vue:160 ~ addPage ~ dialog:', data.dialog);
+};
+const edit = async (item: any) => {
+	data.dialog.show = true;
+	data.dialog.title = '编辑管理员';
+	data.form = item;
+	await detail(item.id);
+};
+const detail = async (id: number | string) => {
+	let res = await Admin.detail(id);
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	data.form = res.data;
+};
+const submitForm = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate(async (valid) => {
+		if (!valid) {
+			console.log('error submit!');
+			return false;
+		}
+
+		// console.log('submit!');
+		let res = null;
+		data.dialog.isLoading = true;
+		if (data.form?.id) {
+			//如果是编辑
+			res = await Admin.edit(data.form);
+		} else {
+			res = await Admin.add(data.form);
+		}
+		data.dialog.isLoading = false;
+		if (res.code != 0) {
+			ElMessage.error(res.msg);
+			return;
+		}
+		ElMessage.success(res.msg);
+		search();
+		closePage();
+	});
+};
+
+const deleteOne = (item: any) => {
+	ElMessageBox.confirm('删除后不可恢复,确定要删除该记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete(item.id);
+		})
+		.catch(() => { });
+};
+
+const deleteMult = (item: any) => {
+	if (multipleSelection.value.length == 0) {
+		ElMessage.error('请选择要删除的记录');
+		return;
+	}
+	let ids = multipleSelection.value.map((item: any) => item.id).join(',');
+	ElMessageBox.confirm('删除后不可恢复,确定要删除选中记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete(ids);
+		})
+		.catch(() => { });
+};
+const doDelete = async (ids: string) => {
+	data.loading = true;
+	let res = await Admin.delete(ids);
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	await getList();
+};
+
+const resetPwd = (item: any) => {
+	ElMessageBox.prompt('请填写重置的密码', '重置密码', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		inputPattern: /^[\w_-]{6,16}$/,
+		inputErrorMessage: '密码强度不符合要求!密码最短6位,最长16位,支持字母或数字',
+	})
+		.then(async ({ value }) => {
+			let res = await Admin.resetPwd(item.id, value);
+			if (res.code != 0) {
+				ElMessage.error(res.msg);
+				return;
+			}
+			ElMessage.success(res.msg);
+			//getList();
+		})
+		.catch(() => { });
+};
+
+//=========页面事件
+/**
+ * 页面加载时事件
+ */
+onMounted(async () => {
+	getList();
+	init();
+});
+</script>

+ 295 - 0
h5/src/views/admin/roleManage/edit.vue

@@ -0,0 +1,295 @@
+<template>
+	<div class="system-dept-dialog-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
+			<el-form ref="deptDialogFormRef" :model="state.ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="权限名称">
+							<el-input v-model="state.ruleForm.name" placeholder="请输入权限名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="备注说明">
+							<el-input v-model="state.ruleForm.remark" placeholder="请输入备注说明" clearable></el-input>
+						</el-form-item>
+					</el-col>
+
+					<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色状态">
+							<el-switch v-model="state.status" inline-prompt active-text="启用" inactive-text="禁用"></el-switch>
+						</el-form-item>
+					</el-col> -->
+
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="权限状态" :rules="[{ required: true, message: '权限状态不能为空' }]">
+							<el-radio-group v-model="state.ruleForm.valid">
+								<el-radio :label="1">启用</el-radio>
+								<el-radio :label="0">禁用</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="菜单权限">
+							<!-- :default-expanded-keys="state.ruleForm.codes" -->
+							<el-tree
+								show-checkbox
+							 	ref="treeRef"
+								node-key="id"
+								:data="state.role_list"
+								:props="state.menuProps"
+								:default-checked-keys="state.ruleForm.code"
+								@check-change="getCheckedNodes()"
+								class="menu-data-tree"
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="underlyingProductManageEdit">
+import { reactive, ref } from 'vue';
+import { ElMessage, ElTree } from 'element-plus';
+import type Node from 'element-plus/es/components/tree/src/model/node';
+import { Plus } from '@element-plus/icons-vue';
+import type { UploadProps } from 'element-plus';
+import config from '/@/config.ts';
+import Role from '/@/api/role/role.ts';
+import ruleReg from '/@/utils/ruleReg.ts';
+import Data from './role.ts';
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+
+// 定义变量内容
+const deptDialogFormRef = ref();
+const state = reactive({
+	ruleForm: {
+		id: 0,
+		name: '',
+		remark: '',
+		valid: 1,
+		code:<any> [],
+		codes:<any> [],
+		codes_cn:<any> [],
+	},
+	code:<any> [],
+	codes:<any> [],
+	codes_cn:<any> [],
+	status: true,
+	options: [], // 单位类型
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	action: config.host + '/admin/upload/file',
+	menuData: [] as TreeType[],
+	menuProps: {
+		children: 'children',
+		label: 'label',
+	},
+	role_list: [],//Data.role_list
+});
+
+const handleCheckChange = (
+  data: TreeType,
+  checked: boolean,
+  indeterminate: boolean
+) => {
+  	let getCheckedNodes = treeRef.value!.getCheckedNodes(false, true);
+	console.log('getCheckedNodes', getCheckedNodes);
+	treeFormat(getCheckedNodes);
+}
+
+const treeRef = ref<InstanceType<typeof ElTree>>()
+const getCheckedNodes = () => {
+	let getCheckedNodes = treeRef.value!.getCheckedNodes(false, true);
+	console.log('getCheckedNodes', getCheckedNodes);
+	treeFormat(getCheckedNodes);
+}
+// 树状列表处理
+const treeFormat = (tree_list:any) => {
+	state.code = [];
+	state.codes = [];
+	state.codes_cn = [];
+	let ids:any = [];
+	tree_list.forEach((item:any)=>{
+		state.codes.push(item.id);
+		state.codes_cn.push(item.label);
+	})
+	for(let i=0;i<tree_list.length;i++){
+		for(let j=0;j<tree_list.length;j++){
+			if(tree_list[i].id==tree_list[j].pId){
+				ids.push(tree_list[i].id);
+			}
+		}
+	}
+	for(let k=0;k<tree_list.length;k++){
+		for(let l=0;l<ids.length;l++){
+			if(tree_list[k].id==ids[l]){
+				tree_list.splice(k,1);
+			}
+		}
+	}
+	tree_list.forEach((item:any)=>{
+		state.code.push(item.id);
+	})
+	console.log('state.code',state.code);
+	console.log('state.codes',state.codes);
+	console.log('state.codes_cn',state.codes_cn);
+}
+
+// 打开弹窗
+const openDialog = (type: string, row: any) => {
+	state.dialog.type = type;
+	if (type === 'edit') {
+		state.ruleForm = {
+            id: row.id,
+			name: row.name,
+			remark: row.remark,
+			valid: row.valid,
+			code: row.code,
+			codes: row.codes,
+			codes_cn: row.codes_cn,
+        };
+		state.dialog.title = '编辑权限022';
+		state.dialog.submitTxt = '修 改';
+		state.code = row.code;
+		state.codes = row.codes;
+		state.codes_cn = row.codes_cn;
+		console.log('state.ruleForm.codes->',state.ruleForm.code);
+		console.log('state.ruleForm.codes->',state.ruleForm.codes);
+		console.log('state.ruleForm.codes_cn->',state.ruleForm.codes_cn);
+	} else {
+        state.ruleForm = {
+            id: 0,
+			name: '',
+			remark: '',
+			valid: 1,
+			code: [],
+			codes: [],
+			codes_cn: [],
+        };
+		state.status = true;
+		state.dialog.title = '新增权限021';
+		state.dialog.submitTxt = '保 存';
+		// 清空表单,此项需加表单验证才能使用
+		// nextTick(() => {
+		// 	deptDialogFormRef.value.resetFields();
+		// });
+	}
+	state.dialog.isShowDialog = true;
+	getMenuData();
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+// 提交
+const onSubmit = async() => {
+	if(!ruleReg.emptyReg(state.ruleForm.name)){
+		return ElMessage.warning("请输入权限名称");
+	}
+	if(!ruleReg.emptyReg(state.ruleForm.remark)){
+		return ElMessage.warning("请输入备注说明");
+	}
+	if(state.code[0]!=='home'&&state.codes[0]!=='home'&&state.codes_cn[0]!=='首页'){
+		state.code.unshift('home');
+		state.codes.unshift('home');
+		state.codes_cn.unshift('首页');
+	}
+	state.ruleForm.code = state.code;
+	state.ruleForm.codes = state.codes;
+	state.ruleForm.codes_cn = state.codes_cn;
+	// state.ruleForm.code.push([]);
+	// state.ruleForm.codes.push([]);
+	// state.ruleForm.codes_cn.push([]);
+	// state.ruleForm.code.splice(-1,1);
+	// state.ruleForm.codes.splice(-1,1);
+	// state.ruleForm.codes_cn.splice(-1,1);
+	let res: any = null;
+	if(state.dialog.type=='edit' || state.ruleForm.id!=0){
+		res = await Role.edit(state.ruleForm);
+		if(res.code != 0){
+            return ElMessage.error(res.msg);
+        }
+	}else{
+		res = await Role.add(state.ruleForm);
+		if(res.code != 0){
+            return ElMessage.error(res.msg);
+        }
+	}
+	ElMessage.success(res.msg);
+	closeDialog();
+	emit('refresh');
+};
+
+// 初始化权限菜单
+const init = async() => {
+	let res = await Role.init();
+	if(res.code != 0){
+		return ElMessage.error(res.msg);
+	}
+	state.role_list = res.data;
+};
+
+// 获取菜单结构数据
+const getMenuData = () => {
+	init();
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+	.avatar-uploader .avatar {
+		width: 178px;
+		height: 178px;
+		display: block;
+	}
+	.avatar-uploader .el-upload {
+		border: 1px dashed var(--el-border-color);
+		border-radius: 6px;
+		cursor: pointer;
+		position: relative;
+		overflow: hidden;
+		transition: var(--el-transition-duration-fast);
+	}
+	.avatar-uploader .el-upload:hover {
+		border-color: var(--el-color-primary);
+	}
+	.el-icon.avatar-uploader-icon {
+		font-size: 28px;
+		color: #8c939d;
+		width: 178px;
+		height: 178px;
+		text-align: center;
+	}
+	.system-role-dialog-container {
+		.menu-data-tree {
+			width: 100%;
+			border: 1px solid var(--el-border-color);
+			border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
+			padding: 5px;
+		}
+	}
+</style>

+ 255 - 0
h5/src/views/admin/roleManage/index.vue

@@ -0,0 +1,255 @@
+<template>
+	<div>
+		<el-card class="box-card" v-loading="state.loading" element-loading-text="Loading...">
+            <template #header>
+                <div class="card-header">
+                    <span>权限管理</span>
+                    <div>
+                        <el-button type="default" @click="reFlush()">刷新列表</el-button>
+                        <el-button type="primary" @click="onOpenAddDept('add')">新增权限</el-button>
+                        <el-button type="danger" @click="del()">批量删除</el-button>
+                    </div>
+                </div>
+            </template>
+
+			<el-table ref="multipleTableRef" :data="state.tableData" style="width: 100%" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" />
+				<el-table-column type="index" label="序号" width="90" />
+				<el-table-column property="name" label="权限名称" />
+
+                <el-table-column property="codes_cn" label="权限">
+                    <template #default="scope">
+                        <div v-if="scope.row.codes_cn">
+                            <span v-if="scope.row.codes_cn.length>1">{{scope.row.codes_cn[0]}},{{scope.row.codes_cn[1]}},等{{scope.row.codes_cn.length}}项权限</span>
+                            <span v-else v-for="(item,index) in scope.row.codes_cn" :key="index">
+                                <span>{{item}}</span>
+                            </span>
+                        </div>
+                    </template>
+                </el-table-column>
+
+				<!-- <el-table-column property="company.personnel_size" label="人数" width="90" /> -->
+				<el-table-column property="update_time" label="最后修改时间" width="240" />
+
+                <el-table-column property="valid" label="状态" width="90">
+                    <template #default="scope">
+                        <el-button link type="danger" size="default" v-if="scope.row.valid == 0">禁用</el-button>
+                        <el-button link type="primary" size="default" v-if="scope.row.valid == 1">启用</el-button>
+                    </template>
+                </el-table-column>
+
+				<el-table-column property="remark" label="备注" />
+
+				<el-table-column fixed="right" label="操作" width="120">
+					<template #default="scope">
+						<div class="disflex">
+							<el-button link type="primary" size="small" @click="onOpenEditDept('edit',scope.row)">编辑</el-button>
+							<el-button link type="danger" size="small" @click="handleClick_del(scope.row)">删除</el-button>
+						</div>
+					</template>
+				</el-table-column>
+			</el-table>
+
+            <el-pagination
+				@size-change="onHandleSizeChange"
+				@current-change="onHandleCurrentChange"
+				class="mt15"
+				:pager-count="5"
+				:page-sizes="[10, 20, 30]"
+				v-model:current-page="state.param.page"
+				background
+				v-model:page-size="state.param.list_rows"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="state.total"
+			>
+			</el-pagination>
+		</el-card>
+        <DeptDialog ref="deptDialogRef" @refresh="getTableData()" />
+	</div>
+</template>
+
+<script lang="ts" setup name="underlyingRoleManage">
+    import { ref, reactive, onMounted, defineAsyncComponent } from 'vue';
+    import { ElTable, ElMessage, ElMessageBox } from 'element-plus';
+    import Role from '/@/api/role/role.ts';
+    import ruleReg from '/@/utils/ruleReg';
+
+    const DeptDialog = defineAsyncComponent(() => import('/@/views/underlying/roleManage/edit.vue'));
+    const deptDialogRef = ref();
+    // 打开新增菜单弹窗
+    const onOpenAddDept = (type: string) => {
+        deptDialogRef.value.openDialog(type);
+    };
+    // 打开编辑菜单弹窗
+    const onOpenEditDept = (type: string, row: any) => {
+        row = JSON.parse(JSON.stringify(row));
+        console.log('row',row);
+        deptDialogRef.value.openDialog(type, row);
+    };
+
+    interface User {
+        id: number
+        name: string
+        gender: number
+        img_url: string
+        company_id: number
+        department_id: number
+        id_card: string
+        age: number
+        phone: string
+        password: string
+        role_id: number
+        is_root: number
+        valid: number
+        create_time: string
+        update_time: string
+        is_pass: number
+        pass_time: string
+        remark: string
+    }
+    const multipleTableRef = ref<InstanceType<typeof ElTable>>()
+    const multipleSelection = ref<User[]>([])
+    const toggleSelection = (rows?: User[]) => {
+        if (rows) {
+            rows.forEach((row) => {
+                // TODO: improvement typing when refactor table
+                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+                // @ts-expect-error
+                multipleTableRef.value!.toggleRowSelection(row, undefined)
+            })
+        } else {
+            multipleTableRef.value!.clearSelection()
+        }
+    }
+    const handleSelectionChange = (val: User[]) => {
+        multipleSelection.value = val;
+        console.log('multipleSelection',multipleSelection.value);
+    }
+    const handleCommand = (command: string | number | object) => {
+        console.log('command',command);
+    }
+    const handleClick_edit = () => {
+        ElMessage(`click on 编辑`);
+    }
+    const handleClick_del = (row: any) => {
+        row = JSON.parse(JSON.stringify(row));
+        console.log('row',row);
+        ElMessageBox.confirm(`此操作将永久删除角色:“${row.name}”,是否继续?`, '提示', {
+            confirmButtonText: '确认',
+            cancelButtonText: '取消',
+            type: 'warning',
+        })
+        .then(() => {
+            delOnce(row.id);
+            // getTableData();
+            // ElMessage.success('删除成功');
+        })
+        .catch(() => {});
+    }
+    const del = () => {
+        console.log('multipleSelection.value',multipleSelection.value.length);
+        if(multipleSelection.value.length==0){
+            return ElMessage.warning("请选择要操作的列表");
+        }
+        let name = [];
+        state.ids = [];
+        for(let i=0;i<multipleSelection.value.length;i++){
+            name.push(multipleSelection.value[i].name);
+            state.ids.push(multipleSelection.value[i].id);
+        }
+        ElMessageBox.confirm(`此操作将永久删除角色:“${name}”,是否继续?`, '提示', {
+            confirmButtonText: '确认',
+            cancelButtonText: '取消',
+            type: 'warning',
+        })
+        .then(() => {
+            console.log('ids',JSON.stringify(state.ids).split('[')[1].split(']')[0]);
+            delOnce(JSON.stringify(state.ids).split('[')[1].split(']')[0]);
+            // getTableData();
+            // ElMessage.success('删除成功');
+        })
+        .catch(() => {});
+    }
+    const delOnce = async(ids: number | string) => {
+        let res = await Role.del(ids);
+        if(res.code != 0){
+            return ElMessage.error(res.msg);
+        }
+        getTableData();
+        ElMessage.success(res.msg);
+    }
+
+    const state = reactive({
+        tableData: [],
+        total: 0,
+        loading: false,
+        param: {
+            keyword: '',
+            page: 1,
+            list_rows: 10,
+        },
+        ids: [],
+    });
+    const getList = async() => {
+        let res = await Role.list(state.param);
+        if(res.code != 0){
+            return ElMessage.error(res.msg);
+        }
+        res.data.data.forEach((item:any)=>{
+            // console.log('item.codes_cn1',item.codes_cn);
+            // console.log('item.codes1',item.codes);
+            // console.log('item.code1',item.code);
+            if(!ruleReg.emptyReg(item.codes_cn)){
+                item.codes_cn = [];
+                item.codes = [];
+                item.code = [];
+            }else{
+                item.codes_cn = (item.codes_cn || '').split(',');
+                item.codes = (item.codes || '').split(',');
+                item.code = (item.code || '').split(',');
+            }
+            // console.log('item.codes_cn2',item.codes_cn.length);
+            // console.log('item.codes2',item.codes.length);
+            // console.log('item.code2',item.code.length);
+        });
+        state.tableData = res.data.data;
+        state.total = res.data.total;
+    }
+    const reFlush = () => {
+        getTableData();
+        state.loading = true;
+        setTimeout(() => {
+            state.loading = false;
+            ElMessage.success("刷新列表成功");
+        }, 500);
+    }
+    // 初始化表格数据
+    const getTableData = () => {
+        getList();
+    };
+    // 分页改变
+    const onHandleSizeChange = (val: number) => {
+        state.param.list_rows = val;
+        getTableData();
+    };
+    // 分页改变
+    const onHandleCurrentChange = (val: number) => {
+        state.param.page = val;
+        getTableData();
+    };
+    // 页面加载时
+    onMounted(() => {
+        getTableData();
+    });
+</script>
+
+<style lang="scss" scoped>
+    .box-card {
+        margin: 10px;
+    }
+    .card-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+    }
+</style>

+ 107 - 0
h5/src/views/admin/roleManage/role.ts

@@ -0,0 +1,107 @@
+const Data = {
+    role_list: [
+		{
+			id: 1,
+			label: '系统管理',
+			children: [
+				{
+					id: 11,
+					label: '菜单管理',
+					children: [
+						{
+							id: 111,
+							label: '菜单新增',
+						},
+						{
+							id: 112,
+							label: '菜单修改',
+						},
+						{
+							id: 113,
+							label: '菜单删除',
+						},
+						{
+							id: 114,
+							label: '菜单查询',
+						},
+					],
+				},
+				{
+					id: 12,
+					label: '角色管理',
+					children: [
+						{
+							id: 121,
+							label: '角色新增',
+						},
+						{
+							id: 122,
+							label: '角色修改',
+						},
+						{
+							id: 123,
+							label: '角色删除',
+						},
+						{
+							id: 124,
+							label: '角色查询',
+						},
+					],
+				},
+				{
+					id: 13,
+					label: '用户管理',
+					children: [
+						{
+							id: 131,
+							label: '用户新增',
+						},
+						{
+							id: 132,
+							label: '用户修改',
+						},
+						{
+							id: 133,
+							label: '用户删除',
+						},
+						{
+							id: 134,
+							label: '用户查询',
+						},
+					],
+				},
+			],
+		},
+		{
+			id: 2,
+			label: '权限管理',
+			children: [
+				{
+					id: 21,
+					label: '前端控制',
+					children: [
+						{
+							id: 211,
+							label: '页面权限',
+						},
+						{
+							id: 212,
+							label: '页面权限',
+						},
+					],
+				},
+				{
+					id: 22,
+					label: '后端控制',
+					children: [
+						{
+							id: 221,
+							label: '页面权限',
+						},
+					],
+				},
+			],
+		},
+	],
+}
+export default Data;

+ 759 - 0
h5/src/views/data/good.vue

@@ -0,0 +1,759 @@
+<template>
+	<div class="p20">
+		<!-- 功能区 -->
+		<el-card class="box-card">
+			<el-row>
+				<el-col :span="12">
+					<el-text class="mx-1">关键词:</el-text>
+
+					<el-input class="width200" placeholder="请输入物品名称" v-model="data.params.keyword"
+						@keyup.enter.native="getList"> </el-input>
+
+					<el-button type="primary" class="ml10" @click="getList">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-col>
+				<el-col :span="12" class="tr">
+					<el-button type="primary" @click="addPage">添加</el-button>
+					<el-button type="danger" @click="deleteMult">批量删除</el-button>
+					<el-button type="default" @click="checkAll">查看全部</el-button>
+					<el-button type="default" @click="importExcel()">导入</el-button>
+					<el-button type="default" @click="exportExcel()">导出</el-button>
+					<el-button type="default" @click="enable">启用</el-button>
+					<el-button type="default" @click="forbidden">禁用</el-button>
+				</el-col>
+			</el-row>
+		</el-card>
+		<!-- 主体区 -->
+		<el-row :gutter="20">
+			<el-col :span="16">
+				<el-card class="box-card mt20" v-loading="data.loading">
+					<!-- 列表 -->
+					<el-table :data="data.tableData" @selection-change="handleTableDataSelectionChange">
+						<!-- props绑定数据表的字段,lable填写中文 -->
+						<el-table-column type="selection" width="55" />
+						<el-table-column fixed prop="no" label="物品编码" width="150"
+							:default-sort="{ prop: 'date', order: 'descending' }" />
+						<el-table-column prop="name" label="物品名称" width="150" />
+						<el-table-column prop="desc" label="物品介绍" width="" />
+						<el-table-column prop="goodClass.name" label="物品类别" width="" />
+						<el-table-column prop="unit" label="单位" width="" />
+						<el-table-column prop="spec" label="规格" width="" />
+						<el-table-column prop="valid" label="是否有效" width="">
+							<template #default="scope">
+								<el-tag v-if="scope.row.valid == 1">启用</el-tag>
+								<el-tag type="danger" v-if="scope.row.valid == 0">禁用</el-tag>
+							</template>
+						</el-table-column>
+						<el-table-column fixed="right" label="操作" width="200">
+							<template #default="scope">
+								<el-button link type="primary" @click="openDetail(scope.row)">详情</el-button>
+								<el-button link type="primary" @click="print(scope.row)">打印</el-button>
+								<el-button link type="primary" @click="edit(scope.$index, scope.row)">编辑</el-button>
+								<el-button link type="danger" @click="deleteOne(scope.row)">删除</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+					<!-- 分页 -->
+					<div class="yc_pagebox">
+						<el-pagination v-model:current-page="data.pagination.currentPage"
+							v-model:page-size="data.pagination.pageSize" :page-sizes="getThemeConfig.pageSizeArray"
+							layout="total, sizes, prev, pager, next, jumper" :total="data.pagination.total"
+							@size-change="handleSizeChange" @current-change="handleCurrentChange" />
+					</div>
+				</el-card>
+			</el-col>
+			<el-col :span="8">
+				<el-card class="box-card mt20" v-loading="data.classLoading">
+					<div class="class">
+						<div class="classFont">物品分类</div>
+						<el-button type="primary" @click="addClassPage">新增类别</el-button>
+					</div>
+					<div class="mt20">
+						<el-input class="" placeholder="请输入物品分类名称" v-model="data.classParams.keyword"
+							@keyup.enter.native="getClass"> </el-input>
+					</div>
+					<!-- 列表 -->
+					<el-table :data="data.tableClassData" style="width: 100%" @row-click="checkproduct">
+						<!-- props绑定数据表的字段,lable填写中文 -->
+						<el-table-column prop="name" label="分类名称" width="" />
+						<el-table-column prop="desc" label="分类说明" width="" />
+						<el-table-column fixed="right" label="操作" width="200">
+							<template #default="scope">
+								<el-button link type="primary"
+									@click.stop="classEdit(scope.$index, scope.row)">编辑</el-button>
+								<el-button link type="danger" @click.stop="deleteClassOne(scope.row)">删除</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+				</el-card>
+			</el-col>
+		</el-row>
+		<!-- 物品弹出容器 ,新增和修改的-->
+		<vuecmf-dialog width="1000px" :model_value="data.dialog.show" :title="data.dialog.title" @close="closePage">
+			<template #content>
+				<el-form :model="newForm" label-width="120px" ref="formRef" v-loading="data.dialog.isLoading">
+					<!-- 每个form-item就是一个表单的标签,label是名称,prop和v-model绑定字段名 -->
+					<!-- <el-form-item label="物品图片" prop="img">
+						<el-avatar shape="square" :size="100" fit="fill" :src="newForm.img" />
+						<el-text class="mx-1" type="info"
+							style="margin-top: -80px; margin-left: 20px;">尺寸316*316,小于100k</el-text>
+					</el-form-item> -->
+					<el-row>
+						<el-col :span="12">
+							<el-form-item label="物品编码" prop="no"
+								:rules="[{ required: true, message: '请选择状态', trigger: 'blur' }]">
+								<el-input v-model="newForm.no" placeholder="填写物品代码" />
+							</el-form-item></el-col>
+						<el-col :span="12">
+							<el-form-item label="物品名称" prop="name"
+								:rules="[{ required: true, message: '请选择状态', trigger: 'blur' }]">
+								<el-input v-model="newForm.name" placeholder="填写物品名称" />
+							</el-form-item></el-col>
+					</el-row>
+					<el-row class="mt20">
+						<el-col :span="12">
+							<el-form-item label="物品单位" prop="unit">
+								<el-input v-model="newForm.unit" placeholder="填写物品单位" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="12">
+							<el-form-item label="物品介绍" prop="desc">
+								<el-input v-model="newForm.desc" placeholder="填写物品介绍" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row class="mt20">
+						<el-col :span="12">
+							<el-form-item label="物品规格" prop="spec">
+								<el-input v-model="newForm.spec" placeholder="填写物品规格" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="12">
+							<el-form-item label="物品分类" prop="good_class_id">
+								<el-select v-model="newForm.good_class_id" value-key="id" placeholder="选择物品分类">
+									<el-option v-for="item in data.region" :key="item.id" :label="item.name"
+										:value="item.id" />
+								</el-select>
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-form-item class="mt20" label="状态" prop="valid"
+						:rules="[{ required: true, message: '请选择状态', trigger: 'blur' }]">
+						<el-radio-group v-model="newForm.valid">
+							<el-radio :label="1">启用</el-radio>
+							<el-radio :label="0">禁用</el-radio>
+						</el-radio-group>
+					</el-form-item>
+				</el-form>
+				<div class="height20"></div>
+			</template>
+			<template #footer>
+				<el-button type="default" @click="closePage">取消</el-button>
+				<el-button type="primary" @click="submitForm(formRef)">确定</el-button>
+			</template>
+		</vuecmf-dialog>
+
+		<!-- 详情弹出容器-->
+		<vuecmf-dialog width="1000px" :model_value="data.dialogDetail.showDetail" :title="data.dialogDetail.titleDetail"
+			@close="closePage">
+			<template #content>
+				<el-form :model="newForm" label-width="120px" ref="formRef" v-loading="data.dialogDetail.isLoadingDetail">
+					<!-- 每个form-item就是一个表单的标签,label是名称,prop和v-model绑定字段名 -->
+					<!-- <el-form-item label="物品图片" prop="img">
+						<el-avatar shape="square" :size="100" fit="fill" :src="newForm.img" />
+						<el-text class="mx-1" type="info"
+							style="margin-top: -80px; margin-left: 20px;">尺寸316*316,小于100k</el-text>
+					</el-form-item> -->
+					<el-row>
+						<el-col :span="12">
+							<el-form-item label="物品编码:" prop="no">
+								<el-text class="mx-1">{{ newForm.no }}</el-text>
+							</el-form-item></el-col>
+						<el-col :span="12">
+							<el-form-item label="物品名称:" prop="name">
+								<el-text class="mx-1">{{ newForm.name }}</el-text>
+							</el-form-item></el-col>
+					</el-row>
+					<el-row class="mt20">
+						<el-col :span="12">
+							<el-form-item label="物品单位:" prop="unit">
+								<el-text class="mx-1">{{ newForm.unit }}</el-text>
+							</el-form-item>
+						</el-col>
+						<el-col :span="12">
+							<el-form-item label="物品介绍:" prop="desc">
+								<el-text class="mx-1">{{ newForm.desc }}</el-text>
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row class="mt20">
+						<el-col :span="12">
+							<el-form-item label="物品规格:" prop="spec">
+								<el-text class="mx-1">{{ newForm.spec }}</el-text>
+							</el-form-item>
+						</el-col>
+						<el-col :span="12">
+							<el-form-item label="物品分类:" prop="good_class_id">
+								<el-text class="mx-1">{{ newForm.good_class_id }}</el-text>
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-form-item class="mt20" label="状态:" prop="valid">
+						<el-text class="mx-1">{{ newForm.valid == 1 ? "启用" : "禁用" }}</el-text>
+					</el-form-item>
+				</el-form>
+				<div class="height20"></div>
+			</template>
+		</vuecmf-dialog>
+
+		<!-- 物品分类弹出容器 ,新增和修改的-->
+		<vuecmf-dialog width="1000px" :model_value="data.classDialog.classShow" :title="data.classDialog.classTitle"
+			@close="closePage">
+			<template #content>
+				<el-form :model="data.form" label-width="120px" ref="formRef" v-loading="data.classDialog.classIsLoading">
+					<!-- 每个form-item就是一个表单的标签,label是名称,prop和v-model绑定字段名 -->
+					<el-form-item label="分类名称" prop="name"> <el-input v-model="data.form.name" placeholder="填写分类名称" />
+					</el-form-item>
+					<el-form-item label="分类说明" prop="desc"> <el-input v-model="data.form.desc" placeholder="填写分类说明" />
+					</el-form-item>
+				</el-form>
+				<div class="height20"></div>
+			</template>
+			<template #footer>
+				<el-button type="default" @click="closePage">取消</el-button>
+				<el-button type="primary" @click="submitClassForm(formRef)">确定</el-button>
+			</template>
+		</vuecmf-dialog>
+		<ImportExcelDialog ref="importExcelDialogRef" @refresh="getTableData()" />
+		<ExportExcelDialog ref="exportExcelDialogRef" @refresh="getTableData()" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { defineAsyncComponent, onMounted, reactive, computed, ref, watch } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Good from '/@/api/model/Good'; //gzs:引入模型
+import GoodClass from '/@/api/model/GoodClass';//gzs:引入模型
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { FormInstance, FormRules } from 'element-plus';
+import router from '/@/router';
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+const formRef = ref<FormInstance>();
+const multipleSelection = ref([]);
+//双向绑定的变量,推荐只定义1个
+//注意:所有变量初始化后再使用
+const data = reactive({
+	//请求查询的参数
+	params: {
+		pageSize: getThemeConfig.value.pageSize, //分页大小
+		page: 1, //当前页面
+		keyword: '', //关键字
+		good_class_id: null,
+	},
+	classParams: {
+		pageSize: getThemeConfig.value.pageSize, //分页大小
+		page: 1, //当前页面
+		keyword: '', //关键字
+	},
+	//分页组件的变量
+	pagination: {
+		currentPage: 1,
+		pageSize: getThemeConfig.value.pageSize,
+		total: 100,
+	},
+	loading: false, //是否加载中
+	classPagination: {
+		currentPage: 1,
+		pageSize: getThemeConfig.value.pageSize,
+		total: 100,
+	},
+	classLoading: false, //是否加载中
+	tableData: [], // 表格数据
+	tableClassData: [], //分类表格数据
+	//物品弹出框
+	dialog: {
+		show: false, // 是否显示弹出框,用于最大化、最小化及还原
+		title: '添加', // 弹出框标题
+		isLoading: false, //是否加载中
+		type: "edit" as "submit" | "edit"	// 表单类型 edit|submit
+	},
+	//详情弹出框
+	dialogDetail: {
+		showDetail: false, // 是否显示弹出框,用于最大化、最小化及还原
+		titleDetail: '添加', // 弹出框标题
+		isLoadingDetail: false, //是否加载中
+	},
+	//分类弹出框
+	classDialog: {
+		classShow: false, // 是否显示弹出框,用于最大化、最小化及还原
+		classTitle: '添加', // 弹出框标题
+		classIsLoading: false, //是否加载中
+	},
+	form: {} as any, //分类新增和修改表单数据
+	//扫码枪
+	value: '',
+	//物品分类选择
+	region: [] as any,
+	//导出
+	impparam: {
+		date: "",
+		keyword: "",
+		product_id: "",
+		classes_id: "",
+		page: 1,
+		list_rows: 10,
+		is_sort: 0
+	},
+});
+/**
+ * 监听页数
+ */
+watch(() => [data.params.good_class_id, data.params.keyword], (count, prevCount) => {
+	data.params.page = 1
+	getList();
+}
+)
+
+// 初始化表格数据
+const getTableData = () => {
+	getList();
+	getClass();
+};
+/**
+ * 导入、导出
+ */
+const ImportExcelDialog = defineAsyncComponent(() => import("/@/views/payrollModule/import.vue"));
+const importExcelDialogRef = ref();
+const importExcel = () => {
+	importExcelDialogRef.value.openDialog();
+};
+
+const ExportExcelDialog = defineAsyncComponent(() => import("/@/views/payrollModule/export.vue"));
+const exportExcelDialogRef = ref();
+const exportExcel = () => {
+	// return ElMessage.warning("功能正在完善中");
+	exportExcelDialogRef.value.openDialog(data.impparam);
+};
+/**
+ * 实例化1个表单对象
+ */
+const newForm = reactive({
+	no: '',
+	name: '',
+	desc: '',
+	unit: '',
+	img: '',
+	spec: '',
+	index: 0,
+	good_class_id: '' as number | string,
+	valid: 1,
+});
+/**
+ * 实例化1个分类表单对象
+ */
+const newClassForm = () => {
+	let form = {
+		id: 0,
+		name: '',
+		desc: '',
+		valid: 1,
+	};
+	return form;
+};
+/**
+ * 启用、禁用
+ */
+const enable = async () => {
+	const items = multipleSelection.value.map((item: any) => { item.valid = 1; return item });
+	let res = await Good.edit(items);
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	ElMessage.success(res.msg);
+	getList()
+};
+const forbidden = async () => {
+	const items = multipleSelection.value.map((item: any) => { item.valid = 0; return item });
+	let res = await Good.edit(items);
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	ElMessage.success(res.msg);
+	getList()
+};
+/**
+ * 切换页面大小
+ * @param val
+ */
+const handleSizeChange = (val: number) => {
+	data.params.pageSize = val;
+	data.classParams.pageSize = val;
+	getList();
+	getClass()
+};
+/**
+ * 翻页
+ */
+const handleCurrentChange = (val: number) => {
+	data.params.page = val;
+	getList();
+};
+
+const search = async () => {
+	data.params.page = 1;
+	await getList();
+};
+/**
+ * 页面初始化方法,有就写逻辑,没有可以留空
+ */
+const init = async () => { };
+/**
+ * 获取所有分类数据
+ */
+const getAll = async () => {
+	data.loading = true;
+	const res = await GoodClass.all();
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	console.log("getAllres", res);
+	data.region = res.data
+	console.log("data.region", data.region);
+};
+/**
+ * 获取分类列表数据
+ */
+const getClass = async () => {
+	data.classLoading = true;
+	const res = await GoodClass.list({
+		pageParams: {
+			page: data.classParams.page,
+			size: data.classParams.pageSize,
+		},
+		keyword: data.classParams.keyword
+	});
+	data.classLoading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	console.log("getClass", res);
+	data.tableClassData = res.data.data;
+	//分页数据赋值
+	// data.classPagination.total = res.data.total;
+	// data.classPagination.currentPage = res.data.current_page;
+	// data.classPagination.pageSize = res.data.per_page;
+};
+/**
+ * 点击查看全部
+ */
+const checkAll = async () => {
+	data.params.good_class_id = null
+	getList();
+};
+/**
+ * 点击分类选项查看物品分类
+ */
+const checkproduct = async (row: any) => {
+	console.log("check", row.id);
+	data.params.good_class_id = row.id;
+};
+/**
+ * 获取分页数据
+ */
+const getList = async () => {
+	data.loading = true;
+	const res = await Good.list({
+		pageParams: {
+			page: data.params.page,
+			size: data.params.pageSize,
+		},
+		keyword: data.params.keyword,
+		good_class_id: data.params.good_class_id
+	}); //异步请求获取 分页数据,接口返回,0为成功,非0失败
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	data.tableData = res.data.data;
+	//分页数据赋值
+	data.pagination.total = res.data.total;
+	data.pagination.currentPage = res.data.current_page;
+	data.pagination.pageSize = res.data.per_page;
+};
+// 翻页
+const handleTableDataSelectionChange = (val: any) => {
+	multipleSelection.value = val;
+	console.log('lzj500🚀 ~ file: index.vue:203 ~ handleTableDataSelectionChange ~ val:', val);
+};
+//=========添加 和 编辑 删除
+/**
+ * 关闭弹出框
+ */
+const closePage = () => {
+	data.dialog.show = false;
+	data.dialogDetail.showDetail = false;
+	data.classDialog.classShow = false;
+};
+
+/**
+ * 弹出新增分类页面
+ */
+const addClassPage = () => {
+	data.classDialog.classShow = true;
+	data.classDialog.classTitle = '添加分类'; //gzs:标题
+	data.form = newClassForm();
+	console.log('lzj500🚀 ~ file: index.vue:160 ~ addPage ~ dialog:', data.classDialog);
+};
+/**
+ * 弹出新增页面
+ */
+const addPage = async () => {
+	await getAll();
+	data.dialog.show = true;
+	data.dialog.title = '添加物品'; //gzs:标题
+	data.dialog.type = "submit";
+	newForm.no = '';
+	newForm.desc = '';
+	newForm.good_class_id = '';
+	newForm.img = '';
+	newForm.unit = '';
+	newForm.valid = 1;
+	newForm.spec = '';
+	newForm.name = '';
+	console.log('lzj500🚀 ~ file: index.vue:160 ~ addPage ~ dialog:', data.dialog);
+};
+/**
+ * 弹出 分类编辑页面
+ */
+const classEdit = async (index: number, item: any) => {
+	data.classDialog.classShow = true;
+	data.classDialog.classTitle = '编辑分类';
+	data.form = item;
+};
+/**
+ * 弹出 编辑页面
+ */
+const edit = async (index: number, item: any) => {
+	data.dialog.show = true;
+	data.dialog.title = '编辑物品';
+	data.dialog.type = "edit";
+	newForm.no = item.no;
+	newForm.desc = item.desc;
+	newForm.good_class_id = item.good_class_id;
+	newForm.img = item.img;
+	newForm.unit = item.unit;
+	newForm.valid = item.valid;
+	newForm.spec = item.spec;
+	newForm.name = item.name;
+	newForm.index = item.id;
+	// await detail(item.id);
+};
+/**
+ * 弹出详情页面
+ */
+const openDetail = async (item: any) => {
+	console.log("itemitem", item);
+
+	data.dialogDetail.showDetail = true;
+	data.dialogDetail.titleDetail = '仓库详情';
+	newForm.no = item.no;
+	newForm.desc = item.desc;
+	newForm.good_class_id = item.goodClass.name;
+	newForm.img = item.img;
+	newForm.unit = item.unit;
+	newForm.valid = item.valid;
+	newForm.spec = item.spec;
+	newForm.name = item.name;
+	newForm.index = item.id;
+};
+/**
+ * 打印机打印
+ */
+const print = (item: any) => {
+	// let url = window.location.href.split('/data/good')[0];
+	// setTimeout(() => {
+	// 	window.open(url + `/data/print?no=${item.no}&name=${item.name}`);
+	// }, 500)
+	router.push({ name: 'printGood', query: { no: item.no, name: item.name } });
+};
+/**
+ * 分类的提交 表单,新增和修改都在这里
+ */
+const submitClassForm = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate(async (valid) => {
+		if (!valid) {
+			console.log('error submit!');
+			return false;
+		}
+
+		// console.log('submit!');
+		let res = null;
+		data.classDialog.classIsLoading = true;
+		if (data.form?.id) {
+			//如果是编辑
+			res = await GoodClass.edit(data.form);
+		} else {
+			res = await GoodClass.add(data.form);
+		}
+		data.classDialog.classIsLoading = false;
+		if (res.code != 0) {
+			ElMessage.error(res.msg);
+			return;
+		}
+		ElMessage.success(res.msg);
+		await getClass();
+		search();
+		closePage();
+	});
+};
+/**
+ * 提交 表单,新增和修改都在这里
+ */
+const submitForm = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate(async (valid) => {
+		if (!valid) {
+			console.log('error submit!');
+			return false;
+		}
+		let res = null;
+		data.dialog.isLoading = true;
+		console.log("🚀 ~ file: good.vue:638 ~ formEl.validate ~ newForm:", newForm)
+		if (data.dialog.type == "edit") {
+			//如果是编辑
+			res = await Good.edit([{
+				id: newForm.index,
+				no: newForm.no,
+				name: newForm.name,
+				img: newForm.img,
+				spec: newForm.spec,
+				valid: newForm.valid,
+				unit: newForm.unit,
+				desc: newForm.desc,
+				good_class_id: newForm.good_class_id,
+			}])
+		} else {
+			res = await Good.add(newForm);
+		}
+		data.dialog.isLoading = false;
+		if (res.code != 0) {
+			ElMessage.error(res.msg);
+			return;
+		}
+		ElMessage.success(res.msg);
+		search();
+		closePage();
+	});
+};
+//删除分类1条数据
+const deleteClassOne = (item: any) => {
+	ElMessageBox.confirm('删除后不可恢复,确定要删除该记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doClassDelete(item.id);
+		})
+		.catch(() => { });
+};
+//删除1条数据
+const deleteOne = (item: any) => {
+	ElMessageBox.confirm('删除后不可恢复,确定要删除该记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete([item.id]);
+		})
+		.catch(() => { });
+};
+//删除多条数据
+const deleteMult = (item: any) => {
+	if (multipleSelection.value.length == 0) {
+		ElMessage.error('请选择要删除的记录');
+		return;
+	}
+	let ids = multipleSelection.value.map((item: any) => item.id);
+	ElMessageBox.confirm('删除后不可恢复,确定要删除选中记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete(ids);
+		})
+		.catch(() => { });
+};
+
+//执行分类删除请求
+const doClassDelete = async (id: any) => {
+	data.loading = true;
+	let res = await GoodClass.delete({ id });
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	await getClass();
+};
+//执行删除请求
+const doDelete = async (id: any) => {
+	data.loading = true;
+	let res = await Good.delete({ id });
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	await getList();
+};
+//=========页面事件
+/**
+ * 页面加载时事件
+ */
+onMounted(async () => {
+	getList();
+	getClass();
+	getAll();
+	init();
+});
+
+
+</script>
+<style>
+.class {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.classFont {
+	color: #31373D;
+	font-size: 14px;
+	font-weight: bold;
+}
+
+.active {
+	color: blue;
+}
+</style>

+ 431 - 0
h5/src/views/data/repertory.vue

@@ -0,0 +1,431 @@
+<template>
+	<div class="p20">
+		<!-- 功能区 -->
+		<el-card class="box-card">
+			<el-row>
+				<el-col :span="12">
+					<el-text class="mx-1">关键词:</el-text>
+
+					<el-input class="width160" placeholder="请输入关键词" v-model="data.params.keyword"
+						@keyup.enter.native="getList"> </el-input>
+
+					<el-button type="primary" class="ml10" @click="getList">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-col>
+				<el-col :span="12" class="tr">
+					<el-button type="primary" @click="addPage">新增</el-button>
+					<el-button type="danger" @click="deleteMult">删除</el-button>
+				</el-col>
+			</el-row>
+		</el-card>
+		<!-- 主体区 -->
+		<el-card class="box-card mt20" v-loading="data.loading">
+			<!-- 列表 -->
+			<el-table :data="data.tableData" @selection-change="handleTableDataSelectionChange">
+				<!-- props绑定数据表的字段,lable填写中文 -->
+				<el-table-column prop="name" label="项目名称" width="150" />
+				<el-table-column prop="create_time" label="创建时间" width="" />
+				<el-table-column prop="responsibility_person_id" label="责任人" width="" />
+				<el-table-column prop="source" label="来源" width="" />
+				<el-table-column prop="status" label="状态" width="" />
+				<el-table-column fixed="right" label="操作" width="250">
+					<template #default="scope">
+						<el-button link type="primary" @click="edit(scope.row)">更新</el-button>
+						<el-button link type="primary" @click="openDetail(scope.row)">详情</el-button>
+						<el-button link type="primary">查看合同</el-button>
+						<el-button link type="danger" @click="deleteOne(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<!-- 分页 -->
+			<div class="yc_pagebox">
+				<el-pagination v-model:current-page="data.pagination.currentPage"
+					v-model:page-size="data.pagination.pageSize" :page-sizes="getThemeConfig.pageSizeArray"
+					layout="total, sizes, prev, pager, next, jumper" :total="data.pagination.total"
+					@size-change="handleSizeChange" @current-change="handleCurrentChange" />
+			</div>
+		</el-card>
+		
+		<!-- 弹出容器 新增,更新的-->
+		<vuecmf-dialog width="1000px" :model_value="data.dialog.show" :title="data.dialog.title" @close="closePage">
+			<template #content>
+				<el-form :model="data.form" label-width="120px" ref="formRef" v-loading="data.dialog.isLoading">
+					<!-- 每个form-item就是一个表单的标签,label是名称,prop和v-model绑定字段名 -->
+					<el-row :gutter="20">
+						<el-col :span="12">
+							<el-form-item label="项目名称" prop="name"
+								:rules="[{ required: true, message: '请填写项目名称', trigger: 'blur' }]">
+								<el-input v-model="data.form.name" placeholder="项目名称" />
+							</el-form-item>
+							<el-form-item label="创建时间" prop="create_time"
+								:rules="[{ required: true, message: '填写创建时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.create_time" placeholder="填写创建时间" />
+							</el-form-item>
+							<el-form-item label="来源" prop="source"
+								:rules="[{ required: true, message: '填写来源', trigger: 'blur' }]">
+								<el-input v-model="data.form.source" placeholder="填写来源" />
+							</el-form-item>
+							<el-form-item label="责任人" prop="responsibility_person_id"
+								:rules="[{ required: true, message: '填写责任人', trigger: 'blur' }]">
+								<el-input v-model="data.form.responsibility_person_id" placeholder="填写责任人" />
+							</el-form-item>
+							<el-form-item label="参与人员" prop="participants_id"
+								:rules="[{ required: true, message: '填写参与人员', trigger: 'blur' }]">
+								<el-input v-model="data.form.participants_id" placeholder="填写参与人员" />
+							</el-form-item>
+							<el-form-item label="预估金额" prop="estimated_amount"
+								:rules="[{ required: true, message: '填写预估金额', trigger: 'blur' }]">
+								<el-input v-model="data.form.estimated_amount" placeholder="填写预估金额" />
+							</el-form-item>
+							<el-form-item label="项目开始时间" prop="project_start_date"
+								:rules="[{ required: true, message: '填写项目开始时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.project_start_date" placeholder="填写项目开始时间" />
+							</el-form-item>
+							<el-form-item label="状态" prop="status"
+								:rules="[{ required: true, message: '请选择状态', trigger: 'blur' }]">
+								<el-radio-group v-model="data.form.status">
+									<el-radio :label="0">商务洽谈中</el-radio>
+									<el-radio :label="1">开发中</el-radio>
+									<el-radio :label="2">交付中</el-radio>
+								</el-radio-group>
+							</el-form-item>
+						</el-col>
+						<el-col :span="12">
+							<el-form-item label="项目结束时间" prop="project_end_date"
+								:rules="[{ required: true, message: '填写项目结束时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.project_end_date" placeholder="填写项目结束时间" />
+							</el-form-item>
+							<el-form-item label="约定开发周期" prop="pre_dev_time"
+								:rules="[{ required: true, message: '填写约定开发周期', trigger: 'blur' }]">
+								<el-input v-model="data.form.pre_dev_time" placeholder="填写约定开发周期" />
+							</el-form-item>
+							<el-form-item label="开发开始时间" prop="dev_start_date"
+								:rules="[{ required: true, message: '填写开发开始时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.dev_start_date" placeholder="填写开发开始时间" />
+							</el-form-item>
+							<el-form-item label="开发结束时间" prop="dev_end_date"
+								:rules="[{ required: true, message: '填写开发结束时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.dev_end_date" placeholder="填写开发结束时间" />
+							</el-form-item>
+							<el-form-item label="约定维护周期" prop="pre_maintain_time"
+								:rules="[{ required: true, message: '填写约定维护周期', trigger: 'blur' }]">
+								<el-input v-model="data.form.pre_maintain_time" placeholder="填写约定维护周期" />
+							</el-form-item>
+							<el-form-item label="维护开始时间" prop="maintain_start_date"
+								:rules="[{ required: true, message: '填写维护开始时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.maintain_start_date" placeholder="填写维护开始时间" />
+							</el-form-item>
+							<el-form-item label="维护结束时间" prop="maintain_end_date"
+								:rules="[{ required: true, message: '填写维护结束时间', trigger: 'blur' }]">
+								<el-input v-model="data.form.maintain_end_date" placeholder="填写维护结束时间" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+				<div class="height20"></div>
+			</template>
+			<template #footer>
+				<el-button type="default" @click="closePage">取消</el-button>
+				<el-button type="primary" @click="submitForm(formRef)">提交</el-button>
+			</template>
+		</vuecmf-dialog>
+		<!-- 弹出容器 ,查看详情-->
+		<vuecmf-dialog width="800px" :model_value="data.dialogDetail.showDetail" :title="data.dialogDetail.titleDetail"
+			@close="closePage">
+			<template #content>
+				<el-form :model="data.form" label-width="120px" ref="formRef" v-loading="data.dialogDetail.isLoadingDetail">
+					<el-form-column type="expand">
+						<el-row>
+							<el-col :span="12">
+								<h3 class="mb20">项目详情表:</h3>
+								<div m="4" class="ml20">
+									<p class="mt10" m="t-0 b-2">项目参与人员: {{ data.form.participants_id }}</p>
+									<p class="mt10" m="t-0 b-2">预估金额, 分为单位: {{ data.form.estimated_amount / 100 }} 元</p>
+									<p class="mt10" m="t-0 b-2">项目周期开始时间: {{ data.form.project_start_date }}</p>
+									<p class="mt10" m="t-0 b-2">项目周期结束时间: {{ data.form.project_end_date }}</p>
+									<p class="mt10" m="t-0 b-2">开发开始时间: {{ data.form.dev_start_date }}</p>
+									<p class="mt10" m="t-0 b-2">开发结束时间: {{ data.form.dev_end_date }}</p>
+									<p class="mt10" m="t-0 b-2">约定开发周期,单位为天: {{ data.form.pre_dev_time }} 天</p>
+									<p class="mt10" m="t-0 b-2">维护开始时间: {{ data.form.maintain_start_date }}</p>
+									<p class="mt10" m="t-0 b-2">维护结束时间: {{ data.form.maintain_end_date }}</p>
+									<p class="mt10" m="t-0 b-2">约定维护周期,单位为天: {{ data.form.pre_maintain_time }}天</p>
+								</div>
+							</el-col>
+							<el-col :span="12">
+								<el-timeline>
+									<el-timeline-item v-for="item in data.form.schedules" :timestamp="item.start_date"
+										placement="top">
+										<el-card>
+											<h3>{{ item.name }}</h3>
+											<h4 class="mt5 mb10">{{ item.desc }}</h4>
+											<p>{{ item.updater }}</p>
+											<div class="text">结束时间:{{ item.end_date }}</div>
+										</el-card>
+
+									</el-timeline-item>
+									<el-timeline-item>
+										<div class="flex justify-space-between mb-4 flex-wrap gap-4">
+											<el-button type="primary" text>更新进度</el-button>
+										</div>
+									</el-timeline-item>
+								</el-timeline>
+							</el-col>
+						</el-row>
+						<div class="height20"></div>
+					</el-form-column>
+				</el-form>
+			</template>
+		</vuecmf-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, reactive, computed, ref } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Repertory from '/@/api/model/Repertory'; //gzs:引入模型
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { FormInstance } from 'element-plus';
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+const formRef = ref<FormInstance>();
+const multipleSelection = ref([]);
+//双向绑定的变量,推荐只定义1个
+//注意:所有变量初始化后再使用
+let data = reactive({
+	//请求查询的参数
+	params: {
+		pageSize: getThemeConfig.value.pageSize, //分页大小
+		page: 1, //当前页面
+		keyword: '', //关键字
+		state: [], //状态
+	},
+	//分页组件的变量
+	pagination: {
+		currentPage: 1,
+		pageSize: getThemeConfig.value.pageSize,
+		total: 100,
+	},
+	loading: false, //是否加载中
+	tableData: [], // 表格数据
+	//弹出框
+	dialog: {
+		show: false, // 是否显示弹出框,用于最大化、最小化及还原
+		title: '添加', // 弹出框标题
+		isLoading: false, //是否加载中
+	},
+	//详情弹出框
+	dialogDetail: {
+		showDetail: false, // 是否显示弹出框,用于最大化、最小化及还原
+		titleDetail: '添加', // 弹出框标题
+		isLoadingDetail: false, //是否加载中
+	},
+	form: {} as any, //新增和修改表单数据
+});
+
+/**
+ * 实例化1个表单对象
+ */
+const newForm = () => {
+	let form = {
+		desc: '',
+		responsibility_person_id: '',
+		name: '',
+		create_time: '',
+		source: '',
+		participants_id: [],
+		estimated_amount: '',
+		status: '',
+		pre_dev_time: '',
+		project_start_date: '',
+		project_end_date: '',
+		dev_start_date: '',
+		dev_end_date: '',
+		pre_maintain_time: '',
+		maintain_start_date: '',
+		maintain_end_date: '',
+	};
+	return form;
+};
+/**
+ * 切换页面大小
+ * @param val
+ */
+const handleSizeChange = (val: number) => {
+	data.params.pageSize = val;
+	getList();
+};
+/**
+ * 翻页
+ */
+const handleCurrentChange = (val: number) => {
+	data.params.page = val;
+	getList();
+};
+
+const search = async () => {
+	data.params.page = 1;
+	await getList();
+};
+/**
+ * 页面初始化方法,有就写逻辑,没有可以留空
+ */
+const init = async () => { };
+/**
+ * 获取分页数据
+ */
+const getList = async () => {
+	data.loading = true;
+	const res = await Repertory.list({
+		pageParams: {
+			size: data.params.pageSize,
+			page: data.params.page
+		},
+		keyword: data.params.keyword,
+		status: [
+
+		],
+	}); //异步请求获取 分页数据,接口返回,0为成功,非0失败
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	data.tableData = res.data.data;
+	//分页数据赋值
+	data.pagination.total = res.data.total;
+	data.pagination.currentPage = res.data.current_page;
+	data.pagination.pageSize = res.data.per_page;
+	console.log('lzj500🚀 ~ file: index.vue:95 ~ getList ~ res:', res);
+};
+// 翻页
+const handleTableDataSelectionChange = (val: any) => {
+	multipleSelection.value = val;
+	console.log('lzj500🚀 ~ file: index.vue:203 ~ handleTableDataSelectionChange ~ val:', val);
+};
+//=========添加 和 编辑 删除
+/**
+ * 关闭弹出框
+ */
+const closePage = () => {
+	data.dialog.show = false;
+	data.dialogDetail.showDetail = false;
+};
+/**
+ * 弹出新增页面
+ */
+const addPage = () => {
+	data.dialog.show = true;
+	data.dialog.title = '添加项目'; //gzs:标题
+	data.form = newForm();
+};
+/**
+ * 弹出 编辑页面
+ */
+const edit = async (item: any) => {
+	data.dialog.show = true;
+	data.dialog.title = '编辑仓库';
+	data.form = item;
+	// await detail(item.id);
+};
+/**
+ * 获取详情
+ */
+const openDetail = async (item: any) => {
+	data.dialogDetail.showDetail = true;
+	data.dialogDetail.titleDetail = '仓库详情';
+	data.form = item;
+};
+/**
+ * 提交 表单,新增和修改都在这里
+ */
+const submitForm = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate(async (valid) => {
+		if (!valid) {
+			console.log('error submit!');
+			return false;
+		}
+		let res = null;
+		data.dialog.isLoading = true;
+		if (data.form?.id) {
+			//如果是编辑
+			res = await Repertory.edit(data.form);
+		} else {
+			res = await Repertory.add(data.form);
+		}
+		data.dialog.isLoading = false;
+		if (res.code != 0) {
+			ElMessage.error(res.msg);
+			return;
+		}
+		ElMessage.success(res.msg);
+		search();
+		closePage();
+	});
+};
+//删除1条数据
+const deleteOne = (item: any) => {
+	ElMessageBox.confirm('删除后不可恢复,确定要删除该记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete([item.id]);
+		})
+		.catch(() => { });
+};
+//删除多条数据
+const deleteMult = (item: any) => {
+	if (multipleSelection.value.length == 0) {
+		ElMessage.error('请选择要删除的记录');
+		return;
+	}
+	let ids = multipleSelection.value.map((item: any) => item.id);
+	ElMessageBox.confirm('删除后不可恢复,确定要删除选中记录吗?', '警告', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(() => {
+			doDelete(ids);
+		})
+		.catch(() => { });
+};
+//执行删除请求
+const doDelete = async (ids: any) => {
+	data.loading = true;
+	let res = await Repertory.delete(ids);
+	data.loading = false;
+	if (res.code != 0) {
+		ElMessage.error(res.msg);
+		return;
+	}
+	await getList();
+};
+
+//=========页面事件
+/**
+ * 页面加载时事件
+ */
+onMounted(async () => {
+	getList();
+	init();
+});
+</script>
+<style scoped lang="scss">
+.text {
+	text-align: right;
+}
+</style>

+ 6 - 0
h5/src/views/data/status.ts

@@ -0,0 +1,6 @@
+import { reactive } from 'vue';
+interface statusData {
+	key?: string;
+	value?: string;
+}
+export const data = reactive<Array<statusData>>([]);

+ 7 - 266
h5/src/views/home/index.vue

@@ -1,154 +1,6 @@
 <template>
 	<div class="home-container layout-pd">
-		<div class="tbox">
-			<div class="tboxct">
-				<div class="tbimg">
-					<img class="tboxctimg" src="../../assets/pd.png" />
-				</div>
-				<div class="tboxctr">
-					<div class="tboxctrt">今日出入库</div>
-					<div class="tboxctrb">{{ state.menus.ioCount }}</div>
-				</div>
-			</div>
-			<div class="tboxct">
-				<div class="tbimg">
-					<img class="tboxctimg" src="../../assets/ck.png" />
-				</div>
-				<div class="tboxctr">
-					<div class="tboxctrt">盘点</div>
-					<div class="tboxctrb">{{ state.menus.checkCount }}</div>
-				</div>
-			</div>
-			<div class="tboxct">
-				<div class="tbimg">
-					<img class="tboxctimg" src="../../assets/ck.png" />
-				</div>
-				<div class="tboxctr">
-					<div class="tboxctrt"> 调拨</div>
-					<div class="tboxctrb">{{ state.menus.allcationCount }}</div>
-				</div>
-			</div>
-			<div class="tboxct">
-				<div class="tbimg">
-					<img class="tboxctimg" src="../../assets/rk.png" />
-				</div>
-				<div class="tboxctr">
-					<div class="tboxctrt"> 物品总数</div>
-					<div class="tboxctrb">{{ state.menus.goodTotal }}</div>
-				</div>
-			</div>
-			<div class="tboxct">
-				<div class="tbimg">
-					<img class="tboxctimg" src="../../assets/cktal.png" />
-				</div>
-				<div class="tboxctr">
-					<div class="tboxctrt">仓库总数</div>
-					<div class="tboxctrb">{{ state.menus.repoCount }}</div>
-				</div>
-			</div>
-		</div>
-		<el-row gutter="10">
-			<el-col :span="6">
-				<el-card class="box-card mt20">
-					<template #header>
-						<div class="card-header">
-							<span>账号管理</span>
-						</div>
-					</template>
-					<div class="disflex">
-						<div v-for="(item, index) in state.list1" :key="index" class="text mb18 mr15"
-							@click="routerLink(item.path)">
-							<div>
-								<el-image style="width: 100px; height: 100px" :src="item.url" />
-								<div class="text mt10">{{ item.text }}</div>
-							</div>
-						</div>
-					</div>
-				</el-card>
-				<el-card class="box-card mt20">
-					<template #header>
-						<div class="card-header">
-							<span>出入库业务</span>
-						</div>
-					</template>
-					<div class="disflex">
-						<div v-for="(item, index) in state.list3" :key="index" class="text mb18 mr15"
-							@click="routerLink(item.path)">
-							<div>
-								<el-image style="width: 100px; height: 100px" :src="item.url" />
-								<div class="text mt10">{{ item.text }}</div>
-							</div>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-			<el-col :span="6">
-				<el-card class="box-card mt20">
-					<template #header>
-						<div class="card-header">
-							<span>基础数据</span>
-						</div>
-					</template>
-					<div class="disflex">
-						<div v-for="(item, index) in state.list2" :key="index" class="text mb18 mr15"
-							@click="routerLink(item.path)">
-							<div>
-								<el-image style="width: 100px; height: 100px" :src="item.url" />
-								<div class="text mt10">{{ item.text }}</div>
-							</div>
-						</div>
-					</div>
-				</el-card>
-				<el-card class="box-card mt20">
-					<template #header>
-						<div class="card-header">
-							<span>库存报表</span>
-						</div>
-					</template>
-					<div class="disflex">
-						<div v-for="(item, index) in state.list4" :key="index" class="text mb18 mr15"
-							@click="routerLink(item.path)">
-							<div>
-								<el-image style="width: 100px; height: 100px" :src="item.url" />
-								<div class="text mt10">{{ item.text }}</div>
-							</div>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-			<el-col :span="12" class="">
-				<el-card class="box-card mt20">
-					<template #header>
-						<div class="card-header">
-							<span>最新出入库数据</span>
-						</div>
-					</template>
-					<el-card class="box-card ">
-						<!-- 列表 -->
-						<el-table :data="state.tableData" height="374">
-							<!-- props绑定数据表的字段,lable填写中文 -->
-							<el-table-column prop="id" label="序号" width="" />
-							<el-table-column prop="io_id" label="订单号" width="" />
-							<el-table-column prop="good_name" label="物品名称" width="150" />
-							<el-table-column prop="repo_name" label="仓库" width="150" />
-							<el-table-column prop="date" label="日期" width="100" />
-							<el-table-column label="出/入" width="">
-								<template #default="scope">
-									<div style="display: flex; align-items: center">
-										<el-image style="width: 40px;" v-if="scope.row.type == 1"
-											:src="config.file + '/public/static/left.png'" />
-										<el-image style="width: 40px;" v-else
-											:src="config.file + '/public/static/right.png'" />
-									</div>
-								</template>
-							</el-table-column>
-							<el-table-column prop="num" label="数量" width="" />
-						</el-table>
-					</el-card>
-				</el-card>
-			</el-col>
-		</el-row>
-		<div class="box">v1.1.7</div>
+		<div>欢迎进入!广东宇宸信息科技有限公司项目管理系统</div>
 	</div>
 </template>
 
@@ -157,10 +9,6 @@ import { onMounted, reactive, computed } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { storeToRefs } from 'pinia';
 import { useThemeConfig } from '/@/stores/themeConfig';
-import config from '/@/config';
-import router from '/@/router';
-import Repertory from '/@/api/model/Repertory';
-import { Session } from '/@/utils/storage';
 import { useMenuApi } from '/@/api/menu/index';
 
 // 定义变量内容
@@ -175,132 +23,25 @@ const menuApi = useMenuApi();
 
 // 定义变量内容
 const state = reactive({
-	list1: [
-		{
-			url: config.file + '/public/static/zhgl.png',
-			text: '账号管理',
-			path: 'adminMng',
-		},
-	],
-	list2: [
-		{
-			url: config.file + '/public/static/ckgl.png',
-			text: '仓库管理',
-			path: 'dataRepertory',
-		},
-		{
-			url: config.file + '/public/static/wpgl.png',
-			text: '物品管理',
-			path: 'dataGood',
-		},
-	],
-	list3: [
-		{
-			url: config.file + '/public/static/crkgl.png',
-			text: '出入库管理',
-			path: 'orderInout',
-		},
-		{
-			url: config.file + '/public/static/db.png',
-			text: '调拨',
-			path: 'orderTransfer',
-		},
-		{
-			url: config.file + '/public/static/pdd.png',
-			text: '盘点',
-			path: 'orderInventory',
-		},
-	],
-	list4: [
-		{
-			url: config.file + '/public/static/ztkc.png',
-			text: '在途库存',
-			path: 'inventoryRealinventory',
-		},
-		{
-			url: config.file + '/public/static/sskc.png',
-			text: '实时库存',
-			path: 'inventoryIntransit',
-		},
-	],
-	codes: [] as any,
-	menus: [] as any,
-	isNum1: 0,
-	isNum2: 0,
-	isNum3: 0,
-	isNum4: 0,
-	params: {
-		pageSize: getThemeConfig.value.pageSize, //分页大小
-		page: 1, //当前页面
-	},
-	tableData: [] as any,
+
 });
 /**
  * 页面初始化方法,有就写逻辑,没有可以留空
  */
 const init = async () => {
-	let res = await Repertory.IoDetail({
-		pageParams: {
-			page: state.params.page,
-			size: state.params.pageSize
-		}
-	});
-	if (res.code != 0) {
-		ElMessage.error(res.msg);
-		return;
-	}
-	state.tableData = res.data.data;
+
 };
-const routerLink = (path: any) => {
-	router.replace({ name: path });
-}
+
 onMounted(async () => {
-	let menus = await menuApi.getMenuAdmin();
-	// let menuss = Session.get('menus');
-	console.log('menus', menus);
-	state.menus = menus.data
-	await treeFormat(menus.data);
-	await menuAdmin(state.list1, 1);
-	await menuAdmin(state.list2, 2);
-	await menuAdmin(state.list3, 3);
-	await menuAdmin(state.list4, 4);
+
 })
 //权限处理
 const menuAdmin = (menu_list: any, index: number) => {
-	let isNum = 0;
-	menu_list.forEach((item: any) => {
-		item.isShow = false;
-		state.codes.forEach((item2: any) => {
-			if (item.path == item2) {
-				item.isShow = true;
-				isNum += 1;
-			}
-		})
-	})
-	switch (index) {
-		case 1:
-			state.isNum1 = isNum;
-			break;
-		case 2:
-			state.isNum2 = isNum;
-			break;
-		case 3:
-			state.isNum3 = isNum;
-			break;
-		case 4:
-			state.isNum4 = isNum;
-			break;
-	}
-	return menu_list;
+
 }
 // 树状列表处理
 const treeFormat = (tree_list: any) => {
-	tree_list.forEach((item: any) => {
-		state.codes.push(item.name);
-		if (item.children) {
-			treeFormat(item.children);
-		}
-	})
+
 }
 /**
  * 页面加载时事件

+ 168 - 0
h5/src/views/payrollModule/crkexport.vue

@@ -0,0 +1,168 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="400px" draggable
+			:close-on-click-modal="false">
+			<div>确定要导出物品记录表吗?</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">取 消</el-button>
+					<el-button type="primary" @click="onSubmit()">
+						{{ state.dialog.submitTxt }}
+					</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="payrollModuleAttendanceImport">
+import { reactive, ref, nextTick } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import type { ElForm, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import Repertory from '/@/api/model/Repertory';
+import config from '/@/config';
+import { ElLoading } from "element-plus";
+type FormInstance = InstanceType<typeof ElForm>;
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const deptDialogFormRef = ref();
+const upload = ref<UploadInstance>();
+const refForm = ref<FormInstance>();
+const uploadEle = ref<any>(null);
+const state = reactive<any>({
+	productOptions: [],
+	ruleForm: {
+		path: '',
+	},
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	isTips: false,
+	fileUrl: '',
+	uploadUrl: config.host + '/admin/upload/file',
+	download_file_url: config.file + '/static/salaryRateItem.xlsx',
+	param: {},
+});
+
+// 打开弹窗
+const openDialog = (param: any) => {
+	//param 导出参数
+	state.param = param;
+	state.dialog.isShowDialog = true;
+	state.dialog.title = '导出物品记录表';
+	state.dialog.submitTxt = '导 出';
+	console.log("param", param);
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+
+// 清除文件缓存
+const ClearFiles = () => {
+	console.log('uploadEle.value=>', uploadEle.value);
+	if (uploadEle.value != null) {
+		state.isTips = false;
+		state.ruleForm.path = '';
+		uploadEle.value.clearFiles();
+	}
+};
+
+// 导出
+const onSubmit = async () => {
+	const loadingInstance = ElLoading.service({ fullscreen: true, text: "正在导出,请耐心等待" });
+	let res = await Repertory.export(state.param);
+	if (res.code != 0) {
+		nextTick(() => {
+			loadingInstance.close();
+		});
+		return ElMessage.error(res.msg);
+	}
+	state.download_file_url = res.data.url;
+	console.log('state.download_file_url', state.download_file_url);
+	window.open(state.download_file_url);
+	nextTick(() => {
+		loadingInstance.close();
+	});
+	setTimeout(() => {
+		ElMessage.success('导出成功,请留意浏览器下载列表');
+		console.log('submit succ!');
+	}, 500)
+	closeDialog();
+	emit('refresh');
+};
+
+// 上传文件成功的事件
+const handleAvatarSuccess = (res: any, file: any) => {
+	console.log('res=>', res);
+	console.log('file=>', file);
+	state.ruleForm.path = res.data.file;
+	state.fileUrl = res.data.url;
+	state.isTips = true;
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+.down-a {
+	text-decoration: none;
+	color: #FFFFFF;
+}
+
+.tips {
+	font-size: 12px;
+	line-height: 1.2;
+	color: #666;
+}
+
+.two {
+	margin: 8px 0px;
+}
+
+.download-file {
+	margin-top: 10px;
+
+	a {
+		text-decoration: none;
+		color: #409eff;
+	}
+}
+
+.avatar-uploader .avatar {
+	width: 178px;
+	height: 178px;
+	display: block;
+}
+
+.avatar-uploader .el-upload {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	cursor: pointer;
+	position: relative;
+	overflow: hidden;
+	transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+	border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+	font-size: 28px;
+	color: #8c939d;
+	width: 178px;
+	height: 178px;
+	text-align: center;
+}
+</style>

+ 171 - 0
h5/src/views/payrollModule/export.vue

@@ -0,0 +1,171 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="400px" draggable
+			:close-on-click-modal="false">
+			<div>确定要导出物品记录表吗?</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">取 消</el-button>
+					<el-button type="primary" @click="onSubmit()">
+						{{ state.dialog.submitTxt }}
+					</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="payrollModuleAttendanceImport">
+import { reactive, ref, nextTick } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import type { ElForm, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { UploadFilled } from '@element-plus/icons-vue';
+import noPacking from '/@/api/noPacking/noPacking';
+import Good from '/@/api/model/Good'; //gzs:引入模型
+import config from '/@/config';
+import { ElLoading } from "element-plus";
+type FormInstance = InstanceType<typeof ElForm>;
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const deptDialogFormRef = ref();
+
+const upload = ref<UploadInstance>();
+const refForm = ref<FormInstance>();
+const uploadEle = ref<any>(null);
+const state = reactive<any>({
+	productOptions: [],
+	ruleForm: {
+		path: '',
+	},
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	isTips: false,
+	fileUrl: '',
+	uploadUrl: config.host + '/admin/upload/file',
+	download_file_url: config.file + '/static/salaryRateItem.xlsx',
+	param: {},
+});
+
+// 打开弹窗
+const openDialog = (param: any) => {
+	//param 导出参数
+	state.param = param;
+	state.dialog.isShowDialog = true;
+	state.dialog.title = '导出物品记录表';
+	state.dialog.submitTxt = '导 出';
+	console.log("param", param);
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+
+// 清除文件缓存
+const ClearFiles = () => {
+	console.log('uploadEle.value=>', uploadEle.value);
+	if (uploadEle.value != null) {
+		state.isTips = false;
+		state.ruleForm.path = '';
+		uploadEle.value.clearFiles();
+	}
+};
+
+// 导出
+const onSubmit = async () => {
+	const loadingInstance = ElLoading.service({ fullscreen: true, text: "正在导出,请耐心等待" });
+	let res = await Good.export(state.param);
+	if (res.code != 0) {
+		nextTick(() => {
+			loadingInstance.close();
+		});
+		return ElMessage.error(res.msg);
+	}
+	state.download_file_url = res.data.url;
+	console.log('state.download_file_url', state.download_file_url);
+	window.open(state.download_file_url);
+	nextTick(() => {
+		loadingInstance.close();
+	});
+	setTimeout(() => {
+		ElMessage.success('导出成功,请留意浏览器下载列表');
+		console.log('submit succ!');
+	}, 500)
+	closeDialog();
+	emit('refresh');
+};
+
+// 上传文件成功的事件
+const handleAvatarSuccess = (res: any, file: any) => {
+	console.log('res=>', res);
+	console.log('file=>', file);
+	state.ruleForm.path = res.data.file;
+	state.fileUrl = res.data.url;
+	state.isTips = true;
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+.down-a {
+	text-decoration: none;
+	color: #FFFFFF;
+}
+
+.tips {
+	font-size: 12px;
+	line-height: 1.2;
+	color: #666;
+}
+
+.two {
+	margin: 8px 0px;
+}
+
+.download-file {
+	margin-top: 10px;
+
+	a {
+		text-decoration: none;
+		color: #409eff;
+	}
+}
+
+.avatar-uploader .avatar {
+	width: 178px;
+	height: 178px;
+	display: block;
+}
+
+.avatar-uploader .el-upload {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	cursor: pointer;
+	position: relative;
+	overflow: hidden;
+	transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+	border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+	font-size: 28px;
+	color: #8c939d;
+	width: 178px;
+	height: 178px;
+	text-align: center;
+}
+</style>

+ 223 - 0
h5/src/views/payrollModule/import.vue

@@ -0,0 +1,223 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="800px" draggable
+			:close-on-click-modal="false">
+			<div>第一步:请点击下面的链接下载Excel模板,并按照模板填写信息。</div>
+			<a :href="state.download_file_url" target="target" download>
+				<el-button class="download-file" key="primary" type="default">
+					下载模板
+				</el-button>
+			</a>
+			<div class="two">第二步:导入Excel模板文件。</div>
+			<el-form :model="state.ruleForm" ref="refForm">
+				<el-form-item label="文件" prop="path" :rules="[{ required: true, message: '请上传文件' }]">
+					<el-upload ref="uploadEle" class="upload-demo" drag :action="state.uploadUrl" :limit="1"
+						:on-exceed="handleExceed" method="post" :on-success="handleAvatarSuccess" :on-remove="ClearFiles"
+						:auto-upload="false" :headers="{ token: state.getToken }">
+						<el-icon class="el-icon--upload">
+							<upload-filled />
+						</el-icon>
+						<div class="el-upload__text">拖拽文件或者 <em>点击上传</em></div>
+						<template #tip>
+							<div class="el-upload__tip" v-if="state.isTips">已上传文件到:{{ state.fileUrl }},请点击导入按钮开始导入</div>
+						</template>
+					</el-upload>
+				</el-form-item>
+			</el-form>
+
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">取 消</el-button>
+					<el-button type="primary" @click="onSubmit()">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="payrollModuleAttendanceImport">
+import { reactive, ref, nextTick } from 'vue';
+import { ElMessageBox, ElMessage, ElLoading, genFileId } from 'element-plus';
+import type { ElForm, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { UploadFilled } from '@element-plus/icons-vue';
+type FormInstance = InstanceType<typeof ElForm>;
+// import noPacking from '/@/api/noPacking/noPacking.ts';
+import Good from '/@/api/model/Good'; //gzs:引入模型
+import config from '/@/config.ts';
+import { Session } from '/@/utils/storage';
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const deptDialogFormRef = ref();
+
+const upload = ref<UploadInstance>();
+const refForm = ref<FormInstance>();
+const uploadEle = ref<any>(null);
+const state = reactive<any>({
+	productOptions: [],
+	ruleForm: {
+		path: '',
+	},
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	isTips: false,
+	fileUrl: '',
+	uploadUrl: config.host + '/admin/good/import',
+	download_file_url: config.file + '/public/static/execl/GOOD_TEMPLATE.xlsx',
+	getToken: Session.get('token')
+});
+
+// 打开弹窗
+const openDialog = () => {
+	state.ruleForm = {
+		path: '',
+	};
+	ClearFiles();
+	state.dialog.isShowDialog = true;
+	state.dialog.title = '导入物品记录表';
+	state.dialog.submitTxt = '导 入';
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+
+// 清除文件缓存
+const ClearFiles = () => {
+	console.log('uploadEle.value=>', uploadEle.value);
+	if (uploadEle.value != null) {
+		state.isTips = false;
+		state.ruleForm.path = '';
+		uploadEle.value.clearFiles();
+	}
+};
+
+// 新增
+const onSubmit = () => {
+	uploadEle.value!.submit()
+};
+//替换上一次文件
+const handleExceed: UploadProps['onExceed'] = (files) => {
+	uploadEle.value!.clearFiles()
+	const file = files[0] as UploadRawFile
+	file.uid = genFileId()
+	uploadEle.value!.handleStart(file)
+}
+// const onSubmit = (formEl: FormInstance | undefined) => {
+// 	console.log('state.ruleForm.path', state.ruleForm.path);
+// 	console.log('onSubmit formEl', formEl);
+// 	// if (!state.ruleForm.path) {
+// 	// 	ElMessage.error('请先上传');
+// 	// 	return;
+// 	// }
+// 	if (!formEl) return;
+// 	formEl.validate(async (valid) => {
+// 		console.log('valid', valid);
+// 		if (valid) {
+// 			const loadingInstance = ElLoading.service({ fullscreen: true,text: "正在导入中" })
+// 			let res: any = null;
+// 			res = await Good.import(state.ruleForm);
+// 			if (res.code != 0) {
+// 				nextTick(() => {
+// 					loadingInstance.close()
+// 				})
+// 				clearData();
+// 				closeDialog();
+// 				ElMessage.error(res.msg);
+// 				return;
+// 			}
+// 			clearData();
+// 			closeDialog();
+// 			ElMessage.success(res.msg);
+// 			console.log('submit succ!');
+// 			emit('refresh');
+// 			nextTick(() => {
+// 				loadingInstance.close()
+// 			})
+// 		} else {
+// 			ElMessage.error('导入失败,请检查表格是否正确');
+// 			console.log('error submit!');
+// 			return false;
+// 		}
+// 	});
+// };
+
+const clearData = () => {
+	state.isTips = false;
+	state.ruleForm.path = '';
+	uploadEle.value.clearFiles();
+}
+
+// 上传文件成功的事件
+const handleAvatarSuccess = (res: any, file: any) => {
+	if (res.code == 0) {
+		ElMessage.success('上传成功!');
+		console.log('res=>', res);
+		console.log('file=>', file);
+		state.ruleForm.path = res.data.file;
+		state.fileUrl = res.data.url;
+		state.isTips = true;
+	}
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+.tips {
+	font-size: 12px;
+	line-height: 1.2;
+	color: #666;
+}
+
+.two {
+	margin: 8px 0px;
+}
+
+a {
+	text-decoration: none;
+	color: #409eff;
+}
+
+.download-file {
+	margin-top: 10px;
+}
+
+.avatar-uploader .avatar {
+	width: 178px;
+	height: 178px;
+	display: block;
+}
+
+.avatar-uploader .el-upload {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	cursor: pointer;
+	position: relative;
+	overflow: hidden;
+	transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+	border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+	font-size: 28px;
+	color: #8c939d;
+	width: 178px;
+	height: 178px;
+	text-align: center;
+}
+</style>

+ 170 - 0
h5/src/views/payrollModule/kcexport.vue

@@ -0,0 +1,170 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="400px" draggable
+			:close-on-click-modal="false">
+			<div>确定要导出在途库存记录表吗?</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">取 消</el-button>
+					<el-button type="primary" @click="onSubmit()">
+						{{ state.dialog.submitTxt }}
+					</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="payrollModuleAttendanceImport">
+import { reactive, ref, nextTick } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import type { ElForm, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { UploadFilled } from '@element-plus/icons-vue';
+import realinventory from '/@/api/model/realinventory'; //gzs:引入模型
+import config from '/@/config';
+import { ElLoading } from "element-plus";
+type FormInstance = InstanceType<typeof ElForm>;
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const deptDialogFormRef = ref();
+
+const upload = ref<UploadInstance>();
+const refForm = ref<FormInstance>();
+const uploadEle = ref<any>(null);
+const state = reactive<any>({
+	productOptions: [],
+	ruleForm: {
+		path: '',
+	},
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+	isTips: false,
+	fileUrl: '',
+	uploadUrl: config.host + '/admin/upload/file',
+	download_file_url: config.file + '/static/salaryRateItem.xlsx',
+	param: {},
+});
+
+// 打开弹窗
+const openDialog = (param: any) => {
+	//param 导出参数
+	state.param = param;
+	state.dialog.isShowDialog = true;
+	state.dialog.title = '导出库存记录表';
+	state.dialog.submitTxt = '导 出';
+	console.log("param", param);
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+
+// 清除文件缓存
+const ClearFiles = () => {
+	console.log('uploadEle.value=>', uploadEle.value);
+	if (uploadEle.value != null) {
+		state.isTips = false;
+		state.ruleForm.path = '';
+		uploadEle.value.clearFiles();
+	}
+};
+
+// 导出
+const onSubmit = async () => {
+	const loadingInstance = ElLoading.service({ fullscreen: true, text: "正在导出,请耐心等待" });
+	let res = await realinventory.export(state.param);
+	if (res.code != 0) {
+		nextTick(() => {
+			loadingInstance.close();
+		});
+		return ElMessage.error(res.msg);
+	}
+	state.download_file_url = res.data.url;
+	console.log('state.download_file_url', state.download_file_url);
+	window.open(state.download_file_url);
+	nextTick(() => {
+		loadingInstance.close();
+	});
+	setTimeout(() => {
+		ElMessage.success('导出成功,请留意浏览器下载列表');
+		console.log('submit succ!');
+	}, 500)
+	closeDialog();
+	emit('refresh');
+};
+
+// 上传文件成功的事件
+const handleAvatarSuccess = (res: any, file: any) => {
+	console.log('res=>', res);
+	console.log('file=>', file);
+	state.ruleForm.path = res.data.file;
+	state.fileUrl = res.data.url;
+	state.isTips = true;
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style lang="scss" scoped>
+.down-a {
+	text-decoration: none;
+	color: #FFFFFF;
+}
+
+.tips {
+	font-size: 12px;
+	line-height: 1.2;
+	color: #666;
+}
+
+.two {
+	margin: 8px 0px;
+}
+
+.download-file {
+	margin-top: 10px;
+
+	a {
+		text-decoration: none;
+		color: #409eff;
+	}
+}
+
+.avatar-uploader .avatar {
+	width: 178px;
+	height: 178px;
+	display: block;
+}
+
+.avatar-uploader .el-upload {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	cursor: pointer;
+	position: relative;
+	overflow: hidden;
+	transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+	border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+	font-size: 28px;
+	color: #8c939d;
+	width: 178px;
+	height: 178px;
+	text-align: center;
+}
+</style>

+ 77 - 0
h5/src/views/system/baseSettings/components/Setting.vue

@@ -0,0 +1,77 @@
+<script setup lang="ts">
+import { update, content as getContent } from '/@/api/config'
+import { ref } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+interface Props {
+	code: string
+	name: string
+}
+const props = defineProps<Props>()
+
+/**
+ * 设置内容
+ */
+const content = ref<any>({})
+/**
+ * 表单是否展示
+ */
+const show = ref(false)
+/**
+ * 打开表单按钮加载
+ */
+const showFormBtnLoading = ref(false)
+/**
+ * 提交表单按钮加载 
+ */
+const submitFormBtnLoading = ref(false)
+
+/**
+ * 设置表单
+ */
+const showForm = async () => {
+    showFormBtnLoading.value = true;
+    const result = await getContent(props.code)
+    showFormBtnLoading.value = false;
+
+    content.value = result?.data
+    show.value = true
+}
+
+/**
+ * 提交表单
+ */
+const submitForm = async () => {
+    submitFormBtnLoading.value = true;
+    const response = await update(props.code, props.name, content.value)
+    submitFormBtnLoading.value = false;
+
+    if (response.code != 0) {
+        ElMessageBox.alert('更新失败:' + response.msg, '更新失败!')
+    } else {
+        ElMessage.success({
+            message: '更新成功'
+        })
+    }
+    show.value = false
+}
+
+</script>
+
+<template>
+	<el-button v-loading="showFormBtnLoading" class="cfg-btn" size="large" type="success" @click="showForm"> {{ props.name }} </el-button>
+	<el-dialog v-model="show" :title="name" draggable>
+		<el-form :model="content" lable-width="200px">
+			<slot name="form" :content="content"></slot>
+			<el-form-item>
+				<el-button v-loading="submitFormBtnLoading" type="primary" @click="submitForm">提交</el-button>
+				<el-button>取消</el-button>
+			</el-form-item>
+		</el-form>
+	</el-dialog>
+</template>
+
+<style lang="sass">
+.cfg-btn
+    margin-right: 10px
+</style>