diff --git a/hzhub-admin/.pid b/hzhub-admin/.pid index 008b5bf..3cecbeb 100644 --- a/hzhub-admin/.pid +++ b/hzhub-admin/.pid @@ -1 +1 @@ -3357568 +3183821 diff --git a/hzhub-ai/.pid b/hzhub-ai/.pid index dcb6385..8e9574d 100644 --- a/hzhub-ai/.pid +++ b/hzhub-ai/.pid @@ -1 +1 @@ -3999223 +3182697 diff --git a/hzhub-ai/hzhub-common/hzhub-common-translation/src/main/java/org/hzhub/common/translation/core/impl/UserNameTranslationImpl.java b/hzhub-ai/hzhub-common/hzhub-common-translation/src/main/java/org/hzhub/common/translation/core/impl/UserNameTranslationImpl.java index 6c35e49..e67ce2a 100755 --- a/hzhub-ai/hzhub-common/hzhub-common-translation/src/main/java/org/hzhub/common/translation/core/impl/UserNameTranslationImpl.java +++ b/hzhub-ai/hzhub-common/hzhub-common-translation/src/main/java/org/hzhub/common/translation/core/impl/UserNameTranslationImpl.java @@ -20,7 +20,8 @@ public class UserNameTranslationImpl implements TranslationInterface { @Override public String translation(Object key, String other) { if (key instanceof Long id) { - return userService.selectUserNameById(id); + // 返回用户昵称而不是登录账号 + return userService.selectNicknameById(id); } return null; } diff --git a/hzhub-portal-employee/.pid b/hzhub-portal-employee/.pid index 8a8060d..c7b24b7 100644 --- a/hzhub-portal-employee/.pid +++ b/hzhub-portal-employee/.pid @@ -1 +1 @@ -3422048 +3183908 diff --git a/hzhub-portal-employee/README.md b/hzhub-portal-employee/README.md index a0c4eb4..cca134c 100644 --- a/hzhub-portal-employee/README.md +++ b/hzhub-portal-employee/README.md @@ -146,4 +146,4 @@ A: 开发环境通过Vite代理,生产环境通过Nginx代理配置 `UPSTREAM_ *用 ❤️ 打造,由 HZHub 团队维护* - \ No newline at end of file + diff --git a/hzhub-portal-employee/TIMEOUT_CONFIG.md b/hzhub-portal-employee/TIMEOUT_CONFIG.md index 7ef24c6..7a745c1 100644 --- a/hzhub-portal-employee/TIMEOUT_CONFIG.md +++ b/hzhub-portal-employee/TIMEOUT_CONFIG.md @@ -41,18 +41,18 @@ source /tmp/update_employee_timeout.sql ```sql -- 查询当前配置 -SELECT id, client_id, client_key, active_timeout, timeout, status -FROM sys_client +SELECT id, client_id, client_key, active_timeout, timeout, status +FROM sys_client WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e'; -- 更新为1小时超时 -UPDATE sys_client -SET active_timeout = 3600 +UPDATE sys_client +SET active_timeout = 3600 WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e'; -- 验证结果 -SELECT id, client_id, client_key, active_timeout, timeout, status -FROM sys_client +SELECT id, client_id, client_key, active_timeout, timeout, status +FROM sys_client WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e'; ``` @@ -147,4 +147,4 @@ UPDATE sys_client SET active_timeout = 43200 WHERE client_id = 'e5cd7e4891bf95d1 - [Sa-Token官方文档](https://sa-token.cc/) - [HZHub认证流程](../docs/architecture/README.md) -- [服务管理脚本](./SERVICE_MANAGEMENT.md) \ No newline at end of file +- [服务管理脚本](./SERVICE_MANAGEMENT.md) diff --git a/hzhub-portal-employee/docker-compose-all.yaml b/hzhub-portal-employee/docker-compose-all.yaml index 588f52f..d1f1975 100644 --- a/hzhub-portal-employee/docker-compose-all.yaml +++ b/hzhub-portal-employee/docker-compose-all.yaml @@ -22,7 +22,7 @@ services: container_name: hzhub-ai-mysql restart: always ports: - - "23306:3306" + - '23306:3306' environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: hzhub-ai-agent @@ -30,7 +30,7 @@ services: volumes: - mysql-data:/var/lib/mysql healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"] + test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -proot] interval: 15s timeout: 10s retries: 10 @@ -44,12 +44,12 @@ services: container_name: hzhub-ai-redis restart: always ports: - - "26379:6379" + - '26379:6379' volumes: - redis-data:/data command: redis-server --appendonly yes healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: [CMD, redis-cli, ping] interval: 10s timeout: 5s retries: 5 @@ -62,7 +62,7 @@ services: container_name: hzhub-ai-weaviate restart: always ports: - - "28080:8080" + - '28080:8080' environment: QUERY_DEFAULTS_LIMIT: 25 AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true @@ -81,8 +81,8 @@ services: container_name: hzhub-ai-minio restart: always ports: - - "29000:9000" - - "29090:9090" + - '29000:9000' + - '29090:9090' environment: MINIO_ROOT_USER: ruoyi MINIO_ROOT_PASSWORD: ruoyi123 @@ -98,7 +98,7 @@ services: container_name: hzhub-ai-backend restart: always ports: - - "26039:6039" + - '26039:6039' environment: TZ: Asia/Shanghai # MySQL 配置 @@ -132,7 +132,7 @@ services: container_name: hzhub-ai-admin restart: always ports: - - "25666:5666" + - '25666:5666' environment: # 后端 API 地址 - 运行时动态配置(无需重新构建镜像) # 在完整 docker-compose 中应设置为: http://backend:6039 @@ -158,7 +158,7 @@ services: container_name: hzhub-ai-web restart: always ports: - - "25137:5137" + - '25137:5137' environment: UPSTREAM_URL: http://backend:6039 depends_on: @@ -178,4 +178,4 @@ volumes: weaviate-data: minio-data: logs-data: - upload-data: \ No newline at end of file + upload-data: diff --git a/hzhub-portal-employee/docker-compose.yml b/hzhub-portal-employee/docker-compose.yml index 5d48b90..cab90bc 100644 --- a/hzhub-portal-employee/docker-compose.yml +++ b/hzhub-portal-employee/docker-compose.yml @@ -9,7 +9,7 @@ services: container_name: hzhub-ai-web restart: always ports: - - "5137:5137" + - '5137:5137' environment: # 后端 API 地址 - 运行时通过 nginx 代理 # 在完整 docker-compose 中应设置为: http://backend:6039 diff --git a/hzhub-portal-employee/docs/COMPANY_NAME_CONFIG.md b/hzhub-portal-employee/docs/COMPANY_NAME_CONFIG.md index 8c6057b..b815306 100644 --- a/hzhub-portal-employee/docs/COMPANY_NAME_CONFIG.md +++ b/hzhub-portal-employee/docs/COMPANY_NAME_CONFIG.md @@ -18,8 +18,8 @@ **关键代码**: ```typescript -import { useUserStore } from '@/stores'; import { computed } from 'vue'; +import { useUserStore } from '@/stores'; const userStore = useUserStore(); @@ -49,8 +49,8 @@ const companyName = computed(() => { **关键代码**: ```typescript -import { useRoute } from 'vue-router'; import { computed } from 'vue'; +import { useRoute } from 'vue-router'; const route = useRoute(); @@ -97,7 +97,7 @@ const tenantNameMap: Record = { '000002': '汇亚公司', '000003': '恒福公司', '000004': '玛缇公司', - '000005': '新公司名称', // 添加新公司 + '000005': '新公司名称', // 添加新公司 }; ``` @@ -109,7 +109,7 @@ const tenantNameMap: Record = { ```typescript export interface LoginUser { // ... 现有字段 - companyName?: string; // 新增公司名称字段 + companyName?: string; // 新增公司名称字段 } ``` @@ -166,4 +166,4 @@ const companyName = computed(() => { 1. **统一管理映射关系**:将 `tenantNameMap` 提取到统一的配置文件 2. **支持多语言**:添加国际化支持,显示不同语言的公司名称 3. **动态获取**:从后端API动态获取公司名称,避免硬编码 -4. **Logo定制**:不同公司使用不同的Logo图标 \ No newline at end of file +4. **Logo定制**:不同公司使用不同的Logo图标 diff --git a/hzhub-portal-employee/docs/PROFILE_FEATURE.md b/hzhub-portal-employee/docs/PROFILE_FEATURE.md index f81c7f0..65ea168 100644 --- a/hzhub-portal-employee/docs/PROFILE_FEATURE.md +++ b/hzhub-portal-employee/docs/PROFILE_FEATURE.md @@ -97,28 +97,28 @@ src/ ### 获取用户信息 ```typescript -GET /system/user/profile -Response: UserProfile +GET / system / user / profile; +Response: UserProfile; ``` ### 更新用户信息 ```typescript -PUT /system/user/profile +PUT / system / user / profile; Request: { - userId: number - nickName: string - email: string - phonenumber: string - sex: string + userId: number; + nickName: string; + email: string; + phonenumber: string; + sex: string; } ``` ### 修改密码 ```typescript -PUT /system/user/profile/updatePwd +PUT / system / user / profile / updatePwd; Request: { - oldPassword: string - newPassword: string + oldPassword: string; + newPassword: string; } ``` @@ -195,4 +195,4 @@ Request: FormData (avatarfile: File) ### 兼容性测试 - Chrome、Firefox、Safari 浏览器测试 -- 响应式布局测试(桌面端、平板、移动端) \ No newline at end of file +- 响应式布局测试(桌面端、平板、移动端) diff --git a/hzhub-portal-employee/docs/TABBAR_FEATURE.md b/hzhub-portal-employee/docs/TABBAR_FEATURE.md index ca9b356..5fbeebe 100644 --- a/hzhub-portal-employee/docs/TABBAR_FEATURE.md +++ b/hzhub-portal-employee/docs/TABBAR_FEATURE.md @@ -123,14 +123,14 @@ export function useTabbar() { }, { immediate: true }); return { - currentActive, // 当前激活的标签页 - currentTabs, // 标签页列表 - handleClick, // 点击标签页 - handleClose, // 关闭标签页 - handleUnpin, // 固定/取消固定 - closeOtherTabs, // 关闭其他标签页 - closeAllTabs, // 关闭所有标签页 - closeRightTabs, // 关闭右侧标签页 + currentActive, // 当前激活的标签页 + currentTabs, // 标签页列表 + handleClick, // 点击标签页 + handleClose, // 关闭标签页 + handleUnpin, // 固定/取消固定 + closeOtherTabs, // 关闭其他标签页 + closeAllTabs, // 关闭所有标签页 + closeRightTabs, // 关闭右侧标签页 }; } ``` @@ -149,25 +149,26 @@ const tabs = computed(() => tabbarStore.tabs); const isFullscreen = ref(false); // 点击标签页 -const handleTabClick = (tab: TabItem) => { +function handleTabClick(tab: TabItem) { tabbarStore.setActiveTab(tab.path); router.push(tab.path); -}; +} // 关闭标签页 -const handleTabClose = (path: string) => { +function handleTabClose(path: string) { tabbarStore.closeTab(path); -}; +} // 切换全屏 -const toggleFullscreen = () => { +function toggleFullscreen() { isFullscreen.value = !isFullscreen.value; if (isFullscreen.value) { document.documentElement.requestFullscreen(); - } else { + } + else { document.exitFullscreen(); } -}; +} ``` @@ -175,8 +176,8 @@ const toggleFullscreen = () => { ```vue @@ -355,4 +356,4 @@ const { handleClose, handleUnpin } = useTabbar(); - Chrome、Firefox、Safari 浏览器测试 - 响应式布局测试(桌面端、平板) - 标签页滚动测试(超过 10 个标签页) -- 固定标签页与普通标签页混排测试 \ No newline at end of file +- 固定标签页与普通标签页混排测试 diff --git a/hzhub-portal-employee/src/api/auth/index.ts b/hzhub-portal-employee/src/api/auth/index.ts index 53112bb..ceaa1c9 100644 --- a/hzhub-portal-employee/src/api/auth/index.ts +++ b/hzhub-portal-employee/src/api/auth/index.ts @@ -1,5 +1,5 @@ import type { EmailCodeDTO, LoginDTO, LoginVO, RegisterDTO, TenantResp } from './types'; -import { post, get } from '@/utils/request'; +import { get, post } from '@/utils/request'; export const login = (data: LoginDTO) => post('/auth/login', data).json(); diff --git a/hzhub-portal-employee/src/api/crm/index.ts b/hzhub-portal-employee/src/api/crm/index.ts index 552d7dd..7e48308 100644 --- a/hzhub-portal-employee/src/api/crm/index.ts +++ b/hzhub-portal-employee/src/api/crm/index.ts @@ -9,9 +9,14 @@ import type { CrmLeadBo, CrmLeadFollowBo, CrmLeadFollowVo, + CrmLeadStatsVo, CrmLeadVo, CrmOpportunityBo, CrmOpportunityVo, + CrmSyncAlertVo, + DealerQueryParams, + ErpCustomerSelectVo, + InstantSyncResult, LeadAssignRequest, LeadConvertRequest, LeadQueryParams, @@ -26,9 +31,14 @@ export type { CrmLeadBo, CrmLeadFollowBo, CrmLeadFollowVo, + CrmLeadStatsVo, CrmLeadVo, CrmOpportunityBo, CrmOpportunityVo, + CrmSyncAlertVo, + DealerQueryParams, + ErpCustomerSelectVo, + InstantSyncResult, LeadAssignRequest, LeadConvertRequest, LeadQueryParams, @@ -45,11 +55,18 @@ export function getLeadList(params: LeadQueryParams): Promise> { + return request.get('/crm/lead/stats').json(); +} + /** * 获取线索详情 */ export function getLeadDetail(leadId: number): Promise> { - return request.get(`/crm/lead/${leadId}`).json(); + return request.get(`/crm/lead/detail/${leadId}`).json(); } /** @@ -101,6 +118,20 @@ export function convertLeadToDealer(data: LeadConvertRequest): Promise> return request.post('/crm/lead/convert', data).json(); } +/** + * 作废线索 + */ +export function invalidateLead(leadId: number): Promise> { + return request.put(`/crm/lead/invalidate/${leadId}`).json(); +} + +/** + * 恢复线索 + */ +export function restoreLead(leadId: number): Promise> { + return request.put(`/crm/lead/restore/${leadId}`).json(); +} + /** * ======================================== * CRM 商机管理模块 API 调用 @@ -155,3 +186,58 @@ export function deleteOpportunity(opportunityIds: string): Promise> { export function getDealerSelectList(keyword?: string): Promise> { return request.get('/crm/dealer/portal/select', { keyword }).json(); } + +/** + * ======================================== + * CRM 经销商管理扩展 API + * ======================================== + */ + +/** + * 获取CRM经销商分页列表 + */ +export function getDealerList(params: DealerQueryParams): Promise> { + return request.get('/crm/dealer/portal/list', params).json(); +} + +/** + * 获取经销商详情(含同步状态) + */ +export function getDealerDetail(dealerId: number): Promise> { + return request.get(`/crm/dealer/portal/detail/${dealerId}`).json(); +} + +/** + * ERP客户选择器数据(用于线索转化/经销商创建) + */ +export function getErpCustomerSelect(keyword?: string): Promise> { + return request.get('/crm/dealer/portal/erp-select', { keyword }).json(); +} + +/** + * 校验ERP客户编码是否存在 + */ +export function validateCustomerCode(customerCode: string): Promise> { + return request.get('/crm/dealer/portal/validate-code', { customerCode }).json(); +} + +/** + * 手动同步经销商 + */ +export function manualSyncDealer(dealerId: number): Promise> { + return request.post(`/crm/dealer/portal/sync/${dealerId}`).json(); +} + +/** + * 获取经销商的待处理预警列表 + */ +export function getDealerAlerts(dealerId: number): Promise> { + return request.get(`/crm/dealer/portal/alerts/${dealerId}`).json(); +} + +/** + * 处理预警 + */ +export function resolveAlert(alertId: number, action: string, note?: string): Promise> { + return request.put(`/crm/dealer/portal/alerts/${alertId}`, null, { params: { action, note } }).json(); +} diff --git a/hzhub-portal-employee/src/api/crm/types.ts b/hzhub-portal-employee/src/api/crm/types.ts index b520fd7..33fec84 100644 --- a/hzhub-portal-employee/src/api/crm/types.ts +++ b/hzhub-portal-employee/src/api/crm/types.ts @@ -85,6 +85,22 @@ export interface LeadQueryParams { pageSize: number; } +/** + * 线索统计视图对象 + */ +export interface CrmLeadStatsVo { + totalCount: number; // 线索总数 + highIntentCount: number; // 高意向线索数量 + monthlyNewCount: number; // 本月新增线索数量 + lastMonthNewCount: number; // 上月新增线索数量 + convertedCount: number; // 已转化线索数量 + conversionRate: number; // 转化率(百分比) + monthlyConvertedCount: number; // 本月转化数量 + lastMonthConvertedCount: number; // 上月转化数量 + monthlyHighIntentCount: number; // 本月高意向数量 + lastMonthHighIntentCount: number; // 上月高意向数量 +} + /** * 线索跟进记录视图对象 */ @@ -254,10 +270,72 @@ export interface CrmDealerVo { sourceLeadId?: number; // 来源线索ID status?: string; // 状态 statusName?: string; // 状态名称(翻译) + hasPendingAlerts?: boolean; // 是否有待处理预警(扩展字段) createBy: number; createByName?: string; createTime: string; updateBy?: number; updateByName?: string; updateTime?: string; +} + +/** + * 经销商查询参数 + */ +export interface DealerQueryParams { + dealerName?: string; + dealerCode?: string; + customerCode?: string; + level?: string; + lifecycle?: string; + ownerUserId?: number; + pageNum: number; + pageSize: number; +} + +/** + * ERP客户选择器数据 + */ +export interface ErpCustomerSelectVo { + customerCode: string; + customerName: string; + companyCode?: string; + companyName?: string; + contactName?: string; + phone?: string; + province?: string; + city?: string; + isStop?: number; +} + +/** + * 同步预警视图对象 + */ +export interface CrmSyncAlertVo { + id: number; + tenantId: string; + syncLogId?: number; + dealerId: number; + customerCode: string; + alertType: string; + crmValue?: string; + erpValue?: string; + alertMessage?: string; + status: string; + resolvedBy?: number; + resolvedTime?: string; + resolvedNote?: string; + createTime: string; + dealerName?: string; +} + +/** + * 即时同步结果 + */ +export interface InstantSyncResult { + success: boolean; + message: string; + updated: boolean; + alerts?: CrmSyncAlertVo[]; + erpInfo?: Record; } \ No newline at end of file diff --git a/hzhub-portal-employee/src/api/erp/index.ts b/hzhub-portal-employee/src/api/erp/index.ts index c6cdcb4..bcc2df4 100644 --- a/hzhub-portal-employee/src/api/erp/index.ts +++ b/hzhub-portal-employee/src/api/erp/index.ts @@ -63,7 +63,7 @@ export function getCustomerList(params: { }) { return request.get<{ code: number; msg: string; data: { rows: CustomerVO[]; total: number; code: number; msg: string } }>( '/erp/dynamic/v1/customer/list', - params + params, ).json(); } diff --git a/hzhub-portal-employee/src/api/profile/index.ts b/hzhub-portal-employee/src/api/profile/index.ts index 8e8ce6f..50f31b8 100644 --- a/hzhub-portal-employee/src/api/profile/index.ts +++ b/hzhub-portal-employee/src/api/profile/index.ts @@ -1,4 +1,4 @@ -import type { UserProfile, UpdatePasswordParam } from './types'; +import type { UpdatePasswordParam, UserProfile } from './types'; import { get, put, request } from '@/utils/request'; @@ -37,4 +37,4 @@ export function userUpdateAvatar(file: File) { const formData = new FormData(); formData.append('avatarfile', file); return request.post('/system/user/profile/avatar', formData).json(); -} \ No newline at end of file +} diff --git a/hzhub-portal-employee/src/api/profile/types.ts b/hzhub-portal-employee/src/api/profile/types.ts index c3347b9..43ccfad 100644 --- a/hzhub-portal-employee/src/api/profile/types.ts +++ b/hzhub-portal-employee/src/api/profile/types.ts @@ -65,4 +65,4 @@ export interface UserProfile { export interface UpdatePasswordParam { oldPassword: string; newPassword: string; -} \ No newline at end of file +} diff --git a/hzhub-portal-employee/src/api/wecom/index.ts b/hzhub-portal-employee/src/api/wecom/index.ts index cd3718a..a86ec3a 100644 --- a/hzhub-portal-employee/src/api/wecom/index.ts +++ b/hzhub-portal-employee/src/api/wecom/index.ts @@ -1,20 +1,20 @@ -import { get, post } from '@/utils/request'; import type { - WecomApprovalVo, - WecomApprovalDetail, - ApprovalStats, - WecomTemplate, ApprovalListParams, + ApprovalStats, SubmitApprovalBo, + WecomApprovalDetail, + WecomApprovalVo, + WecomTemplate, } from './types'; +import { get, post } from '@/utils/request'; export type { - WecomApprovalVo, - WecomApprovalDetail, - ApprovalStats, - WecomTemplate, ApprovalListParams, + ApprovalStats, SubmitApprovalBo, + WecomApprovalDetail, + WecomApprovalVo, + WecomTemplate, }; /** 分页查询审批列表 */ diff --git a/hzhub-portal-employee/src/components/LoginDialog/components/FormLogin/AccountPassword.vue b/hzhub-portal-employee/src/components/LoginDialog/components/FormLogin/AccountPassword.vue index dacf2fc..c0fceb7 100644 --- a/hzhub-portal-employee/src/components/LoginDialog/components/FormLogin/AccountPassword.vue +++ b/hzhub-portal-employee/src/components/LoginDialog/components/FormLogin/AccountPassword.vue @@ -2,13 +2,13 @@ @@ -110,131 +496,559 @@ onMounted(() => {
-
+
- + + +
{{ s.value }} {{ s.label }}
- + + + {{ s.change }}
- -
-
-
-
- - {{ stage.label }} - {{ stage.count }} + + + + + +
+
+
+
+ + {{ stage.label }} + {{ stage.count }} +
+
+
+
+ + + 新建客户 +
-
-
- - - 新建客户 + + +
+
+
+ + + {{ stage.label }} + + {{ stage.count }} +
+
+
+
+ {{ opp.opportunityName }} + + 赢单 + + + 线索 + +
+
+ {{ opp.dealerName || '经销商' }} +
+
+
+ + + + {{ opp.ownerUserName || '--' }} +
+
+ + + + {{ opp.expectedCloseDate || '--' }} +
+
+ +
+
+ {{ opp.ownerUserName.charAt(0) }} +
+
+
+
+ + + +
+
+
+
+ + +
+
+

+ 客户列表 +

+ + 管理客户 + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ + + + + + + + + + + + + + 搜索 + + + 新建线索 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + {{ currentLead.companyName }} + + + {{ currentLead.contactName }} + + + {{ currentLead.mobile }} + + + + {{ currentLead.customerCode }} + + -- + + + {{ currentLead.sourceTypeName || '--' }} + + + {{ currentLead.industryName || '--' }} + + + {{ currentLead.storeCount || '--' }} + + + + + + + {{ currentLead.riskLevelName || '--' }} + + + + {{ currentLead.ownerUserName || '--' }} + + + {{ currentLead.leadStatusName || '--' }} + + + {{ currentLead.nextFollowTime || '--' }} + + + {{ currentLead.createTime }} + + + {{ currentLead.remark || '--' }} + + + + + + 跟进记录 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
-
-
- - - {{ stage.label }} - - {{ stage.count }} -
-
-
-
- {{ card.name }} - {{ card.priority }} -
-
{{ card.company }}
-
-
- - {{ card.owner }} -
-
- - {{ card.closeDate }} -
-
- -
-
{{ a.name.charAt(0) }}
-
-
-
- -
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
-
-

客户列表

- 管理客户 -
- - - - - - - - - - - - - - - - -
+ + + + + + + @@ -285,8 +1099,12 @@ onMounted(() => {
@@ -641,6 +1459,71 @@ onMounted(() => { border-radius: 50%; } +/* 线索管理样式(新增) */ +.crm-tabs { + background: #fff; + border-radius: var(--radius-lg); + padding: 20px; + box-shadow: var(--shadow-sm); + margin-bottom: 20px; +} + +.leads-section { + padding: 0; +} + +.leads-filter-bar { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 20px; + flex-wrap: wrap; +} + +.lead-name-cell { + display: flex; + align-items: center; +} + +.follow-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.follow-type { + font-weight: 600; + color: var(--color-text-primary); +} + +.follow-user { + font-size: 12px; + color: var(--color-text-secondary); +} + +.follow-content { + margin-bottom: 8px; + color: var(--color-text-primary); +} + +.follow-ai-summary { + padding: 8px 12px; + background: #f8f7f5; + border-radius: 6px; + font-size: 12px; + color: var(--color-text-secondary); + display: flex; + align-items: center; + gap: 6px; +} + +.follow-next { + margin-top: 8px; + font-size: 12px; + color: var(--color-text-secondary); +} + @media (max-width: 1200px) { .crm-stats { grid-template-columns: repeat(2, 1fr); @@ -660,5 +1543,10 @@ onMounted(() => { .toolbar-right { justify-content: space-between; } + + .leads-filter-bar { + flex-direction: column; + align-items: stretch; + } } diff --git a/hzhub-portal-employee/src/pages/dashboard/components/ChatWidget.vue b/hzhub-portal-employee/src/pages/dashboard/components/ChatWidget.vue index fe489b3..aced1f9 100644 --- a/hzhub-portal-employee/src/pages/dashboard/components/ChatWidget.vue +++ b/hzhub-portal-employee/src/pages/dashboard/components/ChatWidget.vue @@ -1,6 +1,5 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -675,19 +1001,38 @@ onMounted(() => { - 将线索转化为正式经销商,创建经销商档案 + 转化为经销商前,必须绑定ERP客户编码 - - + + + +
+ {{ customer.customerName }} + {{ customer.customerCode }} +
+
+
+
+ + - - - { margin: 0; } +.lead-stats { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + margin-bottom: 20px; +} + +.lead-stat-card { + background: #fff; + border-radius: var(--radius-lg); + padding: 20px; + display: flex; + align-items: center; + gap: 14px; + box-shadow: var(--shadow-sm); + border: 1px solid rgba(0, 0, 0, 0.04); + transition: all 0.25s ease; + + &:hover { + box-shadow: var(--shadow-md); + transform: translateY(-1px); + } +} + +.stat-icon-wrap { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.stat-body { + display: flex; + flex-direction: column; + flex: 1; +} + +.stat-num { + font-size: 20px; + font-weight: 700; + color: var(--color-text-primary); + letter-spacing: -0.02em; +} + +.stat-lbl { + font-size: 12.5px; + color: var(--color-text-secondary); + margin-top: 2px; +} + +.stat-change { + font-size: 12px; + font-weight: 600; + display: flex; + align-items: center; + gap: 2px; + + &.up { color: #16a34a; } + &.down { color: #dc2626; } +} + .leads-filter-bar { display: flex; align-items: center; @@ -798,7 +1207,34 @@ onMounted(() => { color: var(--color-text-secondary); } +.operation-buttons { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: 2px; +} + +.operation-buttons .el-button { + padding: 4px 1px; +} + +.ai-intent-cell { + display: flex; + align-items: center; + gap: 8px; +} + +.ai-score { + font-size: 12px; + color: var(--color-text-secondary); + font-weight: 500; +} + @media (max-width: 768px) { + .lead-stats { + grid-template-columns: repeat(2, 1fr); + } + .leads-filter-bar { flex-direction: column; align-items: stretch; diff --git a/hzhub-portal-employee/src/pages/login/components/TenantAccountPassword.vue b/hzhub-portal-employee/src/pages/login/components/TenantAccountPassword.vue index c6b6ceb..c19e1dd 100644 --- a/hzhub-portal-employee/src/pages/login/components/TenantAccountPassword.vue +++ b/hzhub-portal-employee/src/pages/login/components/TenantAccountPassword.vue @@ -2,11 +2,11 @@ + + \ No newline at end of file + diff --git a/hzhub-portal-employee/src/pages/menu-settings/index.vue b/hzhub-portal-employee/src/pages/menu-settings/index.vue index fbcd510..399b4da 100644 --- a/hzhub-portal-employee/src/pages/menu-settings/index.vue +++ b/hzhub-portal-employee/src/pages/menu-settings/index.vue @@ -1,3 +1,7 @@ + + - - \ No newline at end of file + diff --git a/hzhub-portal-employee/src/pages/profile/components/BasicSetting.vue b/hzhub-portal-employee/src/pages/profile/components/BasicSetting.vue index e613635..da5f317 100644 --- a/hzhub-portal-employee/src/pages/profile/components/BasicSetting.vue +++ b/hzhub-portal-employee/src/pages/profile/components/BasicSetting.vue @@ -1,53 +1,10 @@ - - + + \ No newline at end of file + diff --git a/hzhub-portal-employee/src/pages/profile/components/SecuritySetting.vue b/hzhub-portal-employee/src/pages/profile/components/SecuritySetting.vue index 0dcc377..19433ba 100644 --- a/hzhub-portal-employee/src/pages/profile/components/SecuritySetting.vue +++ b/hzhub-portal-employee/src/pages/profile/components/SecuritySetting.vue @@ -1,3 +1,83 @@ + + - - \ No newline at end of file + diff --git a/hzhub-portal-employee/src/pages/profile/index.vue b/hzhub-portal-employee/src/pages/profile/index.vue index 857f45f..98a4a4e 100644 --- a/hzhub-portal-employee/src/pages/profile/index.vue +++ b/hzhub-portal-employee/src/pages/profile/index.vue @@ -1,103 +1,10 @@ - - + + \ No newline at end of file + diff --git a/hzhub-portal-employee/src/pages/supply/index.vue b/hzhub-portal-employee/src/pages/supply/index.vue index c60d82b..27d8a8b 100644 --- a/hzhub-portal-employee/src/pages/supply/index.vue +++ b/hzhub-portal-employee/src/pages/supply/index.vue @@ -6,8 +6,12 @@