From 830e3611d748980243799231223068b1cd33dc41 Mon Sep 17 00:00:00 2001 From: Ghost <106998207@qq.com> Date: Thu, 23 Apr 2026 16:08:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=81=E4=B8=9A=E7=89=88=E5=90=88?= =?UTF-8?q?=E4=BD=9C=E6=A8=A1=E5=BC=8F=EF=BC=88=E5=90=8E=E7=AB=AF=E3=80=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=AB=AF=E3=80=81=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API:合作模式配置、用户选择、企业版入口与迁移 SQL - 管理端:合作方式管理、企业/用户侧设置与 CSV 导出等 - 小程序:合作模式弹窗与结果/简历等页对接;开发脚本小调整 Made-with: Cursor --- admin/src/components/UserDetailDialog.vue | 49 ++ admin/src/layouts/AdminLayout.vue | 7 +- admin/src/router/index.ts | 6 + admin/src/utils/downloadCsv.ts | 45 ++ admin/src/utils/request.ts | 4 +- admin/src/views/admin/CooperationChoices.vue | 166 +++++++ admin/src/views/admin/Settings.vue | 188 ++++++++ admin/src/views/admin/Users.vue | 64 ++- .../superadmin/CooperationChoicesPanel.vue | 193 ++++++++ admin/src/views/superadmin/EnterpriseHub.vue | 7 +- admin/src/views/superadmin/Enterprises.vue | 176 ++++++- admin/vite.config.ts | 3 +- .../service/EnterpriseCooperationService.php | 450 ++++++++++++++++++ api/app/controller/admin/AppUser.php | 64 +++ .../admin/EnterpriseCooperation.php | 146 ++++++ api/app/controller/api/Cooperation.php | 133 ++++++ .../superadmin/EnterpriseCooperation.php | 127 +++++ api/app/model/EnterpriseCooperationMode.php | 26 + api/app/model/UserCooperationChoice.php | 20 + api/database/add_enterprise_cooperation.sql | 29 ++ api/docs/企业版合作模式需求.md | 184 +++++++ api/route/api.php | 16 + miniprogram/app.js | 4 +- .../cooperation-mode-dialog.js | 77 +++ .../cooperation-mode-dialog.json | 4 + .../cooperation-mode-dialog.wxml | 20 + .../cooperation-mode-dialog.wxss | 104 ++++ miniprogram/pages/index/result.js | 10 +- miniprogram/pages/index/result.json | 3 +- miniprogram/pages/index/result.wxml | 5 + miniprogram/pages/promo/index.json | 14 +- miniprogram/pages/result/mbti.js | 10 +- miniprogram/pages/result/mbti.json | 3 +- miniprogram/pages/result/mbti.wxml | 5 + miniprogram/pages/result/resume.js | 10 +- miniprogram/pages/result/resume.json | 15 +- miniprogram/pages/result/resume.wxml | 5 + miniprogram/utils/enterpriseCooperation.js | 105 ++++ scripts/dev_start.sh | 27 +- scripts/dev_start_daemon.sh | 27 +- 40 files changed, 2518 insertions(+), 33 deletions(-) create mode 100644 admin/src/utils/downloadCsv.ts create mode 100644 admin/src/views/admin/CooperationChoices.vue create mode 100644 admin/src/views/superadmin/CooperationChoicesPanel.vue create mode 100644 api/app/common/service/EnterpriseCooperationService.php create mode 100644 api/app/controller/admin/EnterpriseCooperation.php create mode 100644 api/app/controller/api/Cooperation.php create mode 100644 api/app/controller/superadmin/EnterpriseCooperation.php create mode 100644 api/app/model/EnterpriseCooperationMode.php create mode 100644 api/app/model/UserCooperationChoice.php create mode 100644 api/database/add_enterprise_cooperation.sql create mode 100644 api/docs/企业版合作模式需求.md create mode 100644 miniprogram/components/cooperation-mode-dialog/cooperation-mode-dialog.js create mode 100644 miniprogram/components/cooperation-mode-dialog/cooperation-mode-dialog.json create mode 100644 miniprogram/components/cooperation-mode-dialog/cooperation-mode-dialog.wxml create mode 100644 miniprogram/components/cooperation-mode-dialog/cooperation-mode-dialog.wxss create mode 100644 miniprogram/utils/enterpriseCooperation.js diff --git a/admin/src/components/UserDetailDialog.vue b/admin/src/components/UserDetailDialog.vue index b1352bc..38ba4a6 100644 --- a/admin/src/components/UserDetailDialog.vue +++ b/admin/src/components/UserDetailDialog.vue @@ -51,6 +51,17 @@ +
+
合作意向
+
{{ user.cooperationModeTitle || user.cooperationModeCode }}
+
+ {{ user.cooperationModeCode }} +
+
选择时间 {{ formatDate(user.cooperationChosenAt) }}
+
维度标签
{{ t }} @@ -842,6 +853,44 @@ function openMail(email: string) { margin: 0 4px 4px 0; } +.ud-coop-box { + margin-top: 12px; + padding: 10px 12px; + border-radius: 10px; + background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%); + border: 1px solid #bbf7d0; +} +.ud-coop-box__head { + display: flex; + align-items: center; + gap: 6px; + font-size: 11px; + font-weight: 600; + color: #166534; + margin-bottom: 6px; + .el-icon { + font-size: 14px; + color: #15803d; + } +} +.ud-coop-box__main { + font-size: 13px; + font-weight: 600; + color: #14532d; + line-height: 1.4; +} +.ud-coop-box__code { + margin-top: 4px; + font-size: 11px; + color: #6b7280; + font-family: ui-monospace, monospace; +} +.ud-coop-box__time { + margin-top: 6px; + font-size: 11px; + color: #64748b; +} + .ud-main-pane { flex: 1; min-width: 0; diff --git a/admin/src/layouts/AdminLayout.vue b/admin/src/layouts/AdminLayout.vue index b6de7c6..b6c6f4d 100644 --- a/admin/src/layouts/AdminLayout.vue +++ b/admin/src/layouts/AdminLayout.vue @@ -76,7 +76,8 @@ import { User, ShoppingCart, Share, - Setting + Setting, + Connection } from '@element-plus/icons-vue' const router = useRouter() @@ -90,6 +91,7 @@ const navItems = [ { path: '/admin/users', icon: User, label: '用户运营' }, { path: '/admin/orders', icon: ShoppingCart, label: '订单运营' }, { path: '/admin/distribution', icon: Share, label: '分销推广' }, + { path: '/admin/cooperation-choices', icon: Connection, label: '合作意向' }, { path: '/admin/settings', icon: Setting, label: '企业设置' }, ] @@ -103,6 +105,9 @@ const isActive = (path: string) => { if (path === '/admin/orders') { return route.path === '/admin/orders' } + if (path === '/admin/cooperation-choices') { + return route.path === '/admin/cooperation-choices' + } return route.path === path } diff --git a/admin/src/router/index.ts b/admin/src/router/index.ts index 9bc8004..1835039 100644 --- a/admin/src/router/index.ts +++ b/admin/src/router/index.ts @@ -47,6 +47,12 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/views/admin/Distribution.vue'), meta: { title: '分销推广' } }, + { + path: 'cooperation-choices', + name: 'AdminCooperationChoices', + component: () => import('@/views/admin/CooperationChoices.vue'), + meta: { title: '合作意向' } + }, { path: 'questions', redirect: { path: '/admin/orders', query: { tab: 'questions' } } diff --git a/admin/src/utils/downloadCsv.ts b/admin/src/utils/downloadCsv.ts new file mode 100644 index 0000000..0607964 --- /dev/null +++ b/admin/src/utils/downloadCsv.ts @@ -0,0 +1,45 @@ +import { getApiV1BaseURL } from '@/utils/request' +import { getBearerTokenForCurrentApp } from '@/utils/authStorage' + +/** + * GET 下载 CSV(带当前后台 Bearer;不走 JSON 拦截器,用于 export 等二进制响应) + */ +export async function downloadCsvGet(path: string, filename: string, query?: Record) { + const base = getApiV1BaseURL().replace(/\/$/, '') + const p = path.replace(/^\//, '') + const fullPath = `${base}/${p}`.replace(/\/{2,}/g, '/') + const u = new URL(fullPath, window.location.origin) + if (query) { + Object.entries(query).forEach(([k, v]) => { + if (v === undefined || v === null || v === '') return + u.searchParams.set(k, String(v)) + }) + } + const token = getBearerTokenForCurrentApp() + const res = await fetch(u.toString(), { + headers: token ? { Authorization: `Bearer ${token}` } : {} + }) + if (!res.ok) { + let msg = `HTTP ${res.status}` + try { + const t = await res.text() + if (t) { + try { + const j = JSON.parse(t) + msg = j.message || j.msg || msg + } catch { + msg = t.slice(0, 200) + } + } + } catch { + // ignore + } + throw new Error(msg) + } + const blob = await res.blob() + const a = document.createElement('a') + a.href = URL.createObjectURL(blob) + a.download = filename + a.click() + URL.revokeObjectURL(a.href) +} diff --git a/admin/src/utils/request.ts b/admin/src/utils/request.ts index 709d5ae..ecce784 100644 --- a/admin/src/utils/request.ts +++ b/admin/src/utils/request.ts @@ -19,7 +19,7 @@ function showBizErrorOnce(message: string) { } // 获取API基础URL(开发环境留空 VITE_API_BASE_URL 时走同源 /api/v1,由 Vite 代理到本机后端) -const getBaseURL = (): string => { +export const getApiV1BaseURL = (): string => { const raw = import.meta.env.VITE_API_BASE_URL as string | undefined const envURL = typeof raw === 'string' ? raw.trim() : '' if (envURL) { @@ -28,6 +28,8 @@ const getBaseURL = (): string => { return '/api/v1' } +const getBaseURL = getApiV1BaseURL + // 本地连云库时接口可能较慢:开发环境默认放宽;可用 VITE_REQUEST_TIMEOUT_MS 覆盖 const requestTimeoutMs = (() => { const raw = import.meta.env.VITE_REQUEST_TIMEOUT_MS diff --git a/admin/src/views/admin/CooperationChoices.vue b/admin/src/views/admin/CooperationChoices.vue new file mode 100644 index 0000000..40760a3 --- /dev/null +++ b/admin/src/views/admin/CooperationChoices.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/admin/src/views/admin/Settings.vue b/admin/src/views/admin/Settings.vue index 91d5405..6b1e060 100644 --- a/admin/src/views/admin/Settings.vue +++ b/admin/src/views/admin/Settings.vue @@ -62,6 +62,71 @@
+ +
+
+

合作模式

+

+ 用户在本企业版完成简历、面相与 MBTI 后,可择一合作意向;与超管侧配置一致,仅影响本企业。代码为小写英文、数字、下划线(1–32 位),已保存的代码不可改。 +

+
+
+ + 新增合作模式 +
+ + + + + + + + + + + + + + + + + + + + +

暂无配置,可点击「新增合作模式」添加。

+
+ + 保存合作模式 + +
+
+ + + + + @@ -39,10 +40,11 @@ import { request } from '@/utils/request' import Enterprises from './Enterprises.vue' import Users from './Users.vue' import SoulArticles from './SoulArticles.vue' +import CooperationChoicesPanel from './CooperationChoicesPanel.vue' import SaPageHeader from '@/components/superadmin/SaPageHeader.vue' import SaTabs from '@/components/superadmin/SaTabs.vue' -const TAB_IDS = ['companies', 'users', 'soulArticles'] as const +const TAB_IDS = ['companies', 'users', 'soulArticles', 'cooperation'] as const type TabId = (typeof TAB_IDS)[number] function isTabId(s: string): s is TabId { @@ -96,7 +98,8 @@ async function runOrphanMigrate() { const innerTabs: { label: string; value: TabId }[] = [ { label: '企业列表', value: 'companies' }, { label: '用户总览', value: 'users' }, - { label: '引流文章', value: 'soulArticles' } + { label: '引流文章', value: 'soulArticles' }, + { label: '合作意向', value: 'cooperation' } ] function queryWithoutTab(): Record { diff --git a/admin/src/views/superadmin/Enterprises.vue b/admin/src/views/superadmin/Enterprises.vue index 3aebb32..001a206 100644 --- a/admin/src/views/superadmin/Enterprises.vue +++ b/admin/src/views/superadmin/Enterprises.vue @@ -265,7 +265,7 @@ + + +
+

+ 用户在该企业完成简历、面相与 MBTI 后将弹出选择;关闭项不会对用户展示。代码为小写英文、数字、下划线(1–32 位),保存后不可改。 +

+
+ + 新增合作模式 +
+ + + + + + + + + + + + + + + + + + + + +

暂无配置,可点击「新增合作模式」添加;保存后未包含的旧记录将从数据库删除。

+
+ + 保存合作模式 + +
+
+