diff --git a/go/docs/sql/erp_menus.sql b/go/docs/sql/erp_menus.sql new file mode 100644 index 0000000..a3e4390 --- /dev/null +++ b/go/docs/sql/erp_menus.sql @@ -0,0 +1,192 @@ +-- ============================================================ +-- 进销存(erp)—— 菜单配置(yz_system_menu) +-- 幂等脚本:按 path 判重,重复执行不会产生重复菜单 +-- 执行:mysql -h -P -u -p --default-character-set=utf8mb4 < erp_menus.sql +-- ============================================================ +-- 目标结构(「大类」= 模块根下一层目录,会作为移动端功能页的分组标题; +-- 「小类」= 叶子菜单,渲染为功能卡片): +-- 工作台 -> 工作台(复用已有仪表盘页面 /apps/erp/dashboard) +-- 基础资料 -> 商品 / 供应商 / 客户 / 仓库 +-- 采购管理 -> 采购订单 / 采购入库 / 采购退货 +-- 销售管理 -> 销售订单 / 销售出库 / 销售退货 +-- 库存管理 -> 入库 / 出库 / 调拨 / 盘点 / 库存查询 +-- 财务管理 -> 应收 / 应付 / 收付款 +-- 报表中心 -> 报表中心 +-- 系统管理 -> 系统管理 / 账套管理(账套列表)/ 基础设置 +-- +-- 字段说明: +-- type 1=目录(大类),2=页面(小类) +-- views '[2]' 租户端:Web 租户后台与移动端共用同一份菜单 +-- platforms 留 NULL:展示端继承 erp 模块配置 +-- 未实现的页面 component_path 留空:Web 端为空白页,移动端落到「功能开发中」占位页 +-- icon 完整 Font Awesome 类名;移动端必须落在 uniapp/utils/fa-icons.js 白名单内才显示 + +SET @erp_id := ( + SELECT id FROM yz_system_menu + WHERE path = '/apps/erp' AND delete_time IS NULL + LIMIT 1 +); + +-- ---------- 1. 八个大类(目录) ---------- +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '工作台', '/apps/erp/workbench', NULL, 'fa-solid fa-gauge', 1, 1, 1, '[2]', 1, 'system', '进销存大类:工作台' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/workbench' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '基础资料', '/apps/erp/basic', NULL, 'fa-solid fa-database', 2, 1, 1, '[2]', 1, 'system', '进销存大类:基础资料' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/basic' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '采购管理', '/apps/erp/purchase', NULL, 'fa-solid fa-cart-shopping', 3, 1, 1, '[2]', 1, 'system', '进销存大类:采购管理' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/purchase' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '销售管理', '/apps/erp/sales', NULL, 'fa-solid fa-tags', 4, 1, 1, '[2]', 1, 'system', '进销存大类:销售管理' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/sales' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '库存管理', '/apps/erp/stock', NULL, 'fa-solid fa-warehouse', 5, 1, 1, '[2]', 1, 'system', '进销存大类:库存管理' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/stock' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '财务管理', '/apps/erp/finance', NULL, 'fa-solid fa-coins', 6, 1, 1, '[2]', 1, 'system', '进销存大类:财务管理' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/finance' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '报表中心', '/apps/erp/report', NULL, 'fa-solid fa-chart-column', 7, 1, 1, '[2]', 1, 'system', '进销存大类:报表中心' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/report' AND delete_time IS NULL) x); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT @erp_id, '系统管理', '/apps/erp/system', NULL, 'fa-solid fa-gears', 8, 1, 1, '[2]', 1, 'system', '进销存大类:系统管理' +FROM DUAL +WHERE @erp_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/system' AND delete_time IS NULL) x); + +-- 取回八个大类的ID,供小类挂载 +SET @erp_workbench := (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/workbench' AND delete_time IS NULL LIMIT 1); +SET @erp_basic := (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/basic' AND delete_time IS NULL LIMIT 1); +SET @erp_system := (SELECT id FROM yz_system_menu WHERE path = '/apps/erp/system' AND delete_time IS NULL LIMIT 1); + +-- ---------- 2. 已有菜单迁入新树(避免同一功能出现两份) ---------- +-- 仪表盘 -> 工作台大类,并更名为「工作台」;路由与页面文件不变 +UPDATE yz_system_menu +SET pid = @erp_workbench, title = '工作台', sort = 1, update_time = NOW() +WHERE delete_time IS NULL AND path = '/apps/erp/dashboard' AND @erp_workbench IS NOT NULL; + +-- 供应商列表 / 客户列表 -> 基础资料大类,去掉「列表」后缀;路由与页面文件不变 +UPDATE yz_system_menu +SET pid = @erp_basic, title = '供应商', sort = 2, update_time = NOW() +WHERE delete_time IS NULL AND path = '/apps/erp/suppliers' AND @erp_basic IS NOT NULL; + +UPDATE yz_system_menu +SET pid = @erp_basic, title = '客户', sort = 3, update_time = NOW() +WHERE delete_time IS NULL AND path = '/apps/erp/customers' AND @erp_basic IS NOT NULL; + +-- 账套管理 / 基础设置 -> 系统管理大类 +UPDATE yz_system_menu +SET pid = @erp_system, sort = 2, update_time = NOW() +WHERE delete_time IS NULL AND pid = @erp_id AND title = '账套管理' AND @erp_system IS NOT NULL; + +UPDATE yz_system_menu +SET pid = @erp_system, sort = 3, update_time = NOW() +WHERE delete_time IS NULL AND pid = @erp_id AND title = '基础设置' AND @erp_system IS NOT NULL; + +-- ---------- 3. 新增小类(页面) ---------- +DROP TEMPORARY TABLE IF EXISTS tmp_erp_leaf; +CREATE TEMPORARY TABLE tmp_erp_leaf ( + parent_path VARCHAR(200) NOT NULL, + title VARCHAR(50) NOT NULL, + path VARCHAR(200) NOT NULL, + icon VARCHAR(100) NOT NULL, + sort INT NOT NULL, + remark VARCHAR(500) NOT NULL, + PRIMARY KEY (path) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +INSERT INTO tmp_erp_leaf (parent_path, title, path, icon, sort, remark) VALUES + ('/apps/erp/basic', '商品', '/apps/erp/basic/product', 'fa-solid fa-box', 1, '商品档案'), + ('/apps/erp/basic', '仓库', '/apps/erp/basic/warehouse', 'fa-solid fa-boxes-packing', 4, '仓库档案'), + ('/apps/erp/purchase', '采购订单', '/apps/erp/purchase/order', 'fa-solid fa-file-invoice', 1, '采购订单'), + ('/apps/erp/purchase', '采购入库', '/apps/erp/purchase/inbound', 'fa-solid fa-dolly', 2, '采购入库单'), + ('/apps/erp/purchase', '采购退货', '/apps/erp/purchase/return', 'fa-solid fa-rotate-left', 3, '采购退货单'), + ('/apps/erp/sales', '销售订单', '/apps/erp/sales/order', 'fa-solid fa-file-signature', 1, '销售订单'), + ('/apps/erp/sales', '销售出库', '/apps/erp/sales/outbound', 'fa-solid fa-truck-fast', 2, '销售出库单'), + ('/apps/erp/sales', '销售退货', '/apps/erp/sales/return', 'fa-solid fa-arrow-right-arrow-left',3, '销售退货单'), + ('/apps/erp/stock', '入库', '/apps/erp/stock/inbound', 'fa-solid fa-box-open', 1, '其他入库'), + ('/apps/erp/stock', '出库', '/apps/erp/stock/outbound', 'fa-solid fa-truck', 2, '其他出库'), + ('/apps/erp/stock', '调拨', '/apps/erp/stock/transfer', 'fa-solid fa-right-left', 3, '仓库调拨'), + ('/apps/erp/stock', '盘点', '/apps/erp/stock/taking', 'fa-solid fa-clipboard-check', 4, '库存盘点'), + ('/apps/erp/stock', '库存查询', '/apps/erp/stock/query', 'fa-solid fa-magnifying-glass', 5, '库存查询'), + ('/apps/erp/finance', '应收', '/apps/erp/finance/receivable', 'fa-solid fa-file-invoice-dollar', 1, '应收账款'), + ('/apps/erp/finance', '应付', '/apps/erp/finance/payable', 'fa-solid fa-money-check-dollar', 2, '应付账款'), + ('/apps/erp/finance', '收付款', '/apps/erp/finance/payment', 'fa-solid fa-cash-register', 3, '收付款单'), + ('/apps/erp/report', '报表中心', '/apps/erp/report/index', 'fa-solid fa-chart-line', 1, '进销存报表'), + ('/apps/erp/system', '系统管理', '/apps/erp/system/index', 'fa-solid fa-sliders', 1, '进销存系统设置'); + +INSERT INTO yz_system_menu + (pid, title, path, component_path, icon, sort, status, is_visible, views, type, creater, remark) +SELECT p.id, t.title, t.path, NULL, t.icon, t.sort, 1, 1, '[2]', 2, 'system', t.remark +FROM tmp_erp_leaf t +JOIN yz_system_menu p ON p.path = t.parent_path AND p.delete_time IS NULL +LEFT JOIN yz_system_menu e ON e.path = t.path AND e.delete_time IS NULL +WHERE e.id IS NULL; + +DROP TEMPORARY TABLE IF EXISTS tmp_erp_leaf; + +-- 补图标:迁移过来的菜单原本没有图标,移动端会退化成兜底图标,这里只填空值、不覆盖已有配置 +UPDATE yz_system_menu SET icon = 'fa-solid fa-store', update_time = NOW() +WHERE delete_time IS NULL AND path = '/apps/erp/suppliers' AND (icon IS NULL OR icon = ''); + +UPDATE yz_system_menu SET icon = 'fa-solid fa-address-book', update_time = NOW() +WHERE delete_time IS NULL AND path = '/apps/erp/customers' AND (icon IS NULL OR icon = ''); + +UPDATE yz_system_menu SET icon = 'fa-solid fa-book-bookmark', update_time = NOW() +WHERE delete_time IS NULL AND pid = @erp_system AND title = '账套管理' AND (icon IS NULL OR icon = ''); + +-- ---------- 4. 清理被搬空的两级旧目录(供应商管理 / 客户管理) ---------- +-- 子菜单已迁走(供应商列表 / 客户列表),留着会变成空分组。 +-- 先取出「已无子菜单」的旧目录ID,再按ID软删除:MySQL 不允许在 UPDATE 的子查询里读同一张表。 +SET @erp_empty_dir := NULL; + +SELECT GROUP_CONCAT(d.id) INTO @erp_empty_dir +FROM yz_system_menu d +LEFT JOIN yz_system_menu c ON c.pid = d.id AND c.delete_time IS NULL +WHERE d.pid = @erp_id + AND d.delete_time IS NULL + AND d.title IN ('供应商管理', '客户管理') + AND d.path IN ('/apps/erp/supplier', '/apps/erp/customer') + AND c.id IS NULL; + +UPDATE yz_system_menu +SET delete_time = NOW(), status = 0 +WHERE delete_time IS NULL AND FIND_IN_SET(id, IFNULL(@erp_empty_dir, '')); + +-- ---------- 5. 结果校验(大类 + 其下小类) ---------- +SELECT d.sort AS 大类序号, d.title AS 大类, IFNULL(m.title, '(无)') AS 小类, m.sort AS 小类序号, + m.path AS 路径, m.icon AS 图标, m.component_path AS 页面文件 +FROM yz_system_menu d +LEFT JOIN yz_system_menu m ON m.pid = d.id AND m.delete_time IS NULL +WHERE d.pid = @erp_id AND d.delete_time IS NULL +ORDER BY d.sort, m.sort, m.id; \ No newline at end of file diff --git a/uniapp/components/sub/SubFunctions.vue b/uniapp/components/sub/SubFunctions.vue index 48bb76a..443e449 100644 --- a/uniapp/components/sub/SubFunctions.vue +++ b/uniapp/components/sub/SubFunctions.vue @@ -26,9 +26,7 @@ - {{ group.title }} - {{ group.items.length }} - - + + {{ item.title }} @@ -265,7 +266,9 @@ onShow(() => { word-break: break-all; } -/* ============ 分组卡:外层一个 card,内含若干功能 card ============ */ +/* ============ 分组卡:白色卡片 + 一行 4 个功能宫格(图标在上、名称在下) ============ + cell 占 25% 宽并自带右侧间隙,容器用负 margin 抵消末列间隙, + 避免使用 calc 除法——小程序端 wxss 对复杂 calc 支持不稳。 */ .group-card { @include card; padding: 24rpx 20rpx 8rpx; @@ -282,32 +285,15 @@ onShow(() => { padding: 0 4rpx 20rpx; } -.group-bar { - width: 6rpx; - height: 28rpx; - border-radius: $radius-full; - background: $color-primary; - flex-shrink: 0; -} - .group-title { + @include section-title; flex: 1; min-width: 0; - font-size: 28rpx; - font-weight: 600; - color: $color-text; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.group-count { - @include chip; -} - -/* ============ 功能宫格:一行 4 个 ============ - cell 占 25% 宽并自带右侧间隙,容器用负 margin 抵消末列间隙, - 避免使用 calc 除法——小程序端 wxss 对复杂 calc 支持不稳。 */ .grid { display: flex; flex-wrap: wrap; @@ -317,15 +303,11 @@ onShow(() => { .cell { width: 25%; padding-right: 16rpx; - margin-bottom: 16rpx; + margin-bottom: 24rpx; box-sizing: border-box; } .fn-card { - height: 100%; - padding: 18rpx 6rpx 16rpx; - border-radius: $radius-md; - background: $color-bg-page; display: flex; flex-direction: column; align-items: center; @@ -338,12 +320,12 @@ onShow(() => { } .fn-icon { - @include tint-icon(64rpx, 18rpx); + @include tint-icon(84rpx, 24rpx); } .fn-name { max-width: 100%; - font-size: 22rpx; + font-size: 24rpx; line-height: 1.3; color: $color-text; text-align: center; diff --git a/uniapp/components/sub/SubMine.vue b/uniapp/components/sub/SubMine.vue index 55cf911..ace2501 100644 --- a/uniapp/components/sub/SubMine.vue +++ b/uniapp/components/sub/SubMine.vue @@ -63,7 +63,6 @@ 常用操作 - 3 @@ -82,14 +81,14 @@ 账号与安全 - + @@ -422,11 +421,6 @@ onShow(() => { white-space: nowrap; } -.group-count { - @include chip; - flex-shrink: 0; -} - /* cell 占 25% 并自带右间隙,容器负 margin 抵消末列间隙 */ .grid { display: flex; diff --git a/uniapp/pages.json b/uniapp/pages.json index 08c51a9..66e8424 100644 --- a/uniapp/pages.json +++ b/uniapp/pages.json @@ -289,6 +289,14 @@ "navigationBarTextStyle": "black" } }, + { + "path": "pages/common/developing", + "style": { + "navigationBarTitleText": "功能开发中", + "navigationBarBackgroundColor": "#F8FAFC", + "navigationBarTextStyle": "black" + } + }, { "path": "pages/profile/profile", "style": { diff --git a/uniapp/pages/common/developing.vue b/uniapp/pages/common/developing.vue new file mode 100644 index 0000000..f6b6ca7 --- /dev/null +++ b/uniapp/pages/common/developing.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/uniapp/pages/features/features.vue b/uniapp/pages/features/features.vue index 36985f2..5860e23 100644 --- a/uniapp/pages/features/features.vue +++ b/uniapp/pages/features/features.vue @@ -38,7 +38,7 @@ > - + {{ item.menu.title }} @@ -56,12 +56,8 @@ - - - {{ group.name }} 未开通 - {{ group.items.length }} @@ -74,9 +70,9 @@ - + {{ item.title }} @@ -182,14 +178,15 @@ const groups = computed(() => { if (common.items.length) all.push(common) all.push(...dirGroups) - // 组级「未开通」:整组菜单均被锁定;图标底色按分组下标轮换 - return all.map((group, gi) => { + // 组级「未开通」:整组菜单均被锁定;卡片浅底色与图标色按展开顺序统一编号轮换 + let colorIndex = 0 + return all.map((group) => { const locked = group.items.length > 0 && group.items.every((it) => it.locked === true) return { ...group, icon: group.icon || 'file-lines', - tint: locked ? 'tint-off' : 'tint-' + (gi % 8), - locked + locked, + items: group.items.map((it) => ({ ...it, tintIndex: colorIndex++ })) } }) }) @@ -213,7 +210,6 @@ const searchResults = computed(() => { const mod = modules.value.find((x) => x.code === m.module) return { menu: m, - moduleName: (mod && mod.name) || '协作工具', icon: MODULE_ICONS[String(m.module || '').toLowerCase()] || 'file-lines', tint: mod && mod.enabled === false ? 'tint-off' : 'tint-' + (i % 8), locked: m.locked === true || (mod && mod.enabled === false) @@ -326,7 +322,9 @@ onShow(() => { color: $color-text-muted; } -/* ============ 分组卡:外层一个 card,内含一行 4 个功能 card ============ */ +/* ============ 分组卡:白色卡片 + 一行 4 个功能宫格(图标在上、名称在下) ============ + cell 占 25% 并自带右间隙,容器负 margin 抵消末列间隙, + 避免使用 calc 除法——小程序端 wxss 对复杂 calc 支持不稳。 */ .group-card { @include card; padding: 24rpx 20rpx 8rpx; @@ -343,16 +341,10 @@ onShow(() => { padding: 0 4rpx 20rpx; } -.group-icon { - @include tint-icon(56rpx, 14rpx); -} - .group-title { + @include section-title; flex: 1; min-width: 0; - font-size: 28rpx; - font-weight: 600; - color: $color-text; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -363,13 +355,6 @@ onShow(() => { flex-shrink: 0; } -.group-count { - @include chip; - flex-shrink: 0; -} - -/* 功能格子:一行 4 个;cell 占 25% 并自带右间隙,容器负 margin 抵消末列间隙, - 避免使用 calc 除法——小程序端 wxss 对复杂 calc 支持不稳。 */ .grid { display: flex; flex-wrap: wrap; @@ -379,15 +364,11 @@ onShow(() => { .cell { width: 25%; padding-right: 16rpx; - margin-bottom: 16rpx; + margin-bottom: 24rpx; box-sizing: border-box; } .fn-card { - height: 100%; - padding: 18rpx 6rpx 16rpx; - border-radius: $radius-md; - background: $color-bg-page; display: flex; flex-direction: column; align-items: center; @@ -400,12 +381,12 @@ onShow(() => { } .fn-icon { - @include tint-icon(64rpx, 18rpx); + @include tint-icon(84rpx, 24rpx); } .fn-name { max-width: 100%; - font-size: 22rpx; + font-size: 24rpx; line-height: 1.3; color: $color-text; text-align: center; diff --git a/uniapp/pages/login/login.vue b/uniapp/pages/login/login.vue index d734c24..d95c8de 100644 --- a/uniapp/pages/login/login.vue +++ b/uniapp/pages/login/login.vue @@ -45,20 +45,40 @@ - + 欢迎登录 - {{ showMiniLogin ? '使用微信一键登录,或使用账号密码继续' : '请输入您的账号与密码以继续' }} + {{ panelSub }} {{ errorMsg }} - - + +