diff --git a/.gitignore b/.gitignore index ce8d3de..0cd1a18 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ RTK.md .playwright-mcp .omo .opencode +.workbuddy +specs2 +.shots # Editor directories and files .vscode/* diff --git a/.mock/afterSales.json b/.mock/afterSales.json new file mode 100644 index 0000000..75704b0 --- /dev/null +++ b/.mock/afterSales.json @@ -0,0 +1,31 @@ +{ + "code": 200, + "msg": "ok", + "data": { + "id": "1", + "projectNo": "20260813ZK2Q5f45", + "projectName": "社区康养智能服务系统", + "businessType": "互联网软件开发", + "status": "IN_PROGRESS", + "createTime": "2026-8-17 14:00", + "submitUserName": "张三", + "afterSale": { + "id": "1", + "requirementId": "1", + "type": "BUG问题", + "reason": "具体请看清单", + "remark": null, + "status": "IN_PROGRESS", + "requestUserName": "张三", + "requestTime": "2026-8-17 17:00", + "processUserName": null, + "processNote": null, + "processTime": null, + "withdrawReason": null + }, + "afterSaleAttachments": [ + { "id": "1", "requirementId": "1", "phase": "AFTER_SALE", "ossId": "11", "originalName": "项目需求规格说明书.pdf", "url": "https://example.com/a.pdf", "fileSize": 2516582, "sortOrder": 1, "createTime": "2026-8-17 17:00" }, + { "id": "2", "requirementId": "1", "phase": "AFTER_SALE", "ossId": "12", "originalName": "项目需求说明书.word", "url": "https://example.com/b.docx", "fileSize": 2516582, "sortOrder": 2, "createTime": "2026-8-17 17:00" } + ] + } +} diff --git a/.mock/bills.json b/.mock/bills.json new file mode 100644 index 0000000..a36cb22 --- /dev/null +++ b/.mock/bills.json @@ -0,0 +1,102 @@ +{ + "code": 200, + "msg": "ok", + "data": [ + { + "id": "1001", + "billNo": "20260813Z2Z5f245", + "projectId": "1", + "projectName": "某大型银行数字化转型方案", + "userId": "1", + "amount": 20000, + "billType": "ADVANCE", + "periodName": "第一期 · 付款 比例33%", + "periodRatio": 33, + "status": "PAID", + "payChannel": "WECHAT", + "transactionNo": "T2026081915303578K2M001", + "payTime": "2026-8-17 17:00", + "createTime": "2026-8-17 14:00" + }, + { + "id": "1002", + "billNo": "20260813Z2Z5f245", + "projectId": "2", + "projectName": "智慧城市大数据平台建设项目", + "userId": "1", + "amount": 20000, + "billType": "PROGRESS", + "periodName": "第二期 · 付款 比例33%", + "periodRatio": 33, + "status": "PENDING", + "payChannel": null, + "transactionNo": null, + "payTime": null, + "createTime": "2026-8-17 14:00" + }, + { + "id": "1003", + "billNo": "20260813Z2Z5f245", + "projectId": "3", + "projectName": "三甲医院暗审医疗信息化系统", + "userId": "1", + "amount": 20000, + "billType": "FINAL", + "periodName": "第三期 · 尾款 比例33%", + "periodRatio": 33, + "status": "UNSTARTED", + "payChannel": null, + "transactionNo": null, + "payTime": null, + "createTime": "2026-8-17 14:00" + }, + { + "id": "1004", + "billNo": "20260813Z2Z5f246", + "projectId": "4", + "projectName": "社区康养智能服务系统", + "userId": "1", + "amount": 20000, + "billType": "ADVANCE", + "periodName": "第一期 · 预付款 比例30%", + "periodRatio": 30, + "status": "PAID", + "payChannel": "ALIPAY", + "transactionNo": "T2026081915303578K2M002", + "payTime": "2026-8-18 10:20", + "createTime": "2026-8-17 14:00" + }, + { + "id": "1005", + "billNo": "20260813Z2Z5f247", + "projectId": "5", + "projectName": "智慧园区物联网平台", + "userId": "1", + "amount": 20000, + "billType": "PROGRESS", + "periodName": "第二期 · 进度款 比例35%", + "periodRatio": 35, + "status": "PENDING", + "payChannel": null, + "transactionNo": null, + "payTime": null, + "createTime": "2026-8-17 14:00" + }, + { + "id": "1006", + "billNo": "20260813Z2Z5f248", + "projectId": "6", + "projectName": "某大型银行数字化转型方案", + "userId": "1", + "amount": 20000, + "billType": "ADVANCE", + "periodName": "第一期 · 预付款 比例33%", + "periodRatio": 33, + "status": "UNSTARTED", + "payChannel": null, + "transactionNo": null, + "payTime": null, + "createTime": "2026-8-17 14:00" + } + ] +} diff --git a/.mock/files.json b/.mock/files.json new file mode 100644 index 0000000..f7e357c --- /dev/null +++ b/.mock/files.json @@ -0,0 +1,19 @@ +{ + "code": 200, + "msg": "ok", + "data": { + "id": "1", + "projectNo": "20260813ZK2Q5f45", + "projectName": "社区康养智能服务系统", + "businessType": "互联网软件开发", + "status": "IN_PROGRESS", + "createTime": "2026-8-17 14:00", + "submitUserName": "张三", + "requirementAttachments": [ + { "id": "1", "requirementId": "1", "phase": "REQUIREMENT", "ossId": "1", "originalName": "项目需求规格说明书.pdf", "url": "https://example.com/a.pdf", "fileSize": 2516582, "sortOrder": 1, "createTime": "2026-8-17 17:00" }, + { "id": "2", "requirementId": "1", "phase": "REQUIREMENT", "ossId": "2", "originalName": "项目需求说明书.word", "url": "https://example.com/b.docx", "fileSize": 2516582, "sortOrder": 2, "createTime": "2026-8-17 17:00" }, + { "id": "3", "requirementId": "1", "phase": "REQUIREMENT", "ossId": "3", "originalName": "项目需求规格说明书.pdf", "url": "https://example.com/c.pdf", "fileSize": 2516582, "sortOrder": 3, "createTime": "2026-8-17 17:00" }, + { "id": "4", "requirementId": "1", "phase": "REQUIREMENT", "ossId": "4", "originalName": "项目需求说明书.word", "url": "https://example.com/d.docx", "fileSize": 2516582, "sortOrder": 4, "createTime": "2026-8-17 17:00" } + ] + } +} diff --git a/.mock/finance.json b/.mock/finance.json new file mode 100644 index 0000000..0da1e13 --- /dev/null +++ b/.mock/finance.json @@ -0,0 +1,27 @@ +{ + "code": 200, + "msg": "ok", + "data": { + "id": "1", + "projectNo": "20260813ZK2Q5f45", + "projectName": "社区康养智能服务系统", + "businessType": "互联网软件开发", + "status": "IN_PROGRESS", + "createTime": "2026-8-17 14:00", + "submitUserName": "张三", + "bills": [ + { "id": "1001", "billNo": "2026083222P345", "projectId": "1", "projectNo": "20260813ZK2Q5f45", "projectName": "社区康养智能服务系统", "userId": "1", "amount": "20100.00", "contractId": null, "billType": "ADVANCE", "periodName": "第一期", "periodRatio": "33.00", "status": "PAID", "payChannel": "WECHAT", "transactionNo": "123D06891B30030/8K2M001", "tradeName": null, "payTime": "2026-8-17 17:00", "dueDate": null }, + { "id": "1002", "billNo": "2026083222P345", "projectId": "1", "projectNo": "20260813ZK2Q5f45", "projectName": "社区康养智能服务系统", "userId": "1", "amount": "20100.00", "contractId": null, "billType": "PROGRESS", "periodName": "第二期", "periodRatio": "33.00", "status": "PENDING", "payChannel": null, "transactionNo": null, "tradeName": null, "payTime": null, "dueDate": null }, + { "id": "1003", "billNo": "2026083222P345", "projectId": "1", "projectNo": "20260813ZK2Q5f45", "projectName": "社区康养智能服务系统", "userId": "1", "amount": "20100.00", "contractId": null, "billType": "FINAL", "periodName": "第三期", "periodRatio": "33.00", "status": "UNSTARTED", "payChannel": null, "transactionNo": null, "tradeName": null, "payTime": null, "dueDate": null } + ], + "billPlans": [], + "invoices": [ + { "id": "1", "billId": "1001", "invoiceTitle": "测试", "taxNo": null, "invoiceType": "电子普通发票", "amount": "20100.00", "status": "ISSUED", "invoiceNo": null, "invoiceFile": "https://example.com/inv.pdf", "applyTime": "2026-8-17 17:00", "invoiceDate": null, "createTime": "2026-8-17 17:00" }, + { "id": "2", "billId": "1001", "invoiceTitle": "测试", "taxNo": null, "invoiceType": "电子普通发票", "amount": "20100.00", "status": "ISSUED", "invoiceNo": null, "invoiceFile": "https://example.com/inv2.pdf", "applyTime": "2026-8-17 17:00", "invoiceDate": null, "createTime": "2026-8-17 17:00" } + ], + "contracts": [ + { "id": "1", "contractNo": "HT20260813001", "projectId": "1", "title": "项目需求规格说明书.pdf", "amount": "60300.00", "signStatus": "SIGNED", "status": "SIGNED", "signDate": "2026-8-17", "file": "https://example.com/contract.pdf", "createTime": "2026-8-17 17:00" }, + { "id": "2", "contractNo": "HT20260813002", "projectId": "1", "title": "项目需求说明书.word", "amount": "60300.00", "signStatus": "SIGNED", "status": "SIGNED", "signDate": "2026-8-17", "file": "https://example.com/contract2.docx", "createTime": "2026-8-17 17:00" } + ] + } +} diff --git a/.mock/progress.json b/.mock/progress.json new file mode 100644 index 0000000..7661cdd --- /dev/null +++ b/.mock/progress.json @@ -0,0 +1,42 @@ +{ + "code": 200, + "msg": "ok", + "data": { + "id": "1", + "projectNo": "20260813ZK2Q5f45", + "projectName": "社区康养智能服务系统", + "businessType": "互联网软件开发", + "status": "IN_PROGRESS", + "createTime": "2026-8-17 14:00", + "submitUserName": "张三", + "projectStage": { + "id": "1", + "projectId": "1", + "requirementId": "1", + "stageName": "开发实现", + "status": "IN_PROGRESS", + "startTime": "2026-8-17", + "planTime": "2026-8-17", + "finishTime": null, + "developmentProgress": 65, + "sort": 3 + }, + "developmentProgress": 65, + "projectMilestones": [ + { "id": "1", "projectId": "1", "milestoneName": "需求认证", "sortOrder": 1, "status": "COMPLETED", "startTime": "2026-8-13 11:04:11", "endTime": "2026-8-13 11:04:11", "parallelFlag": false }, + { "id": "2", "projectId": "1", "milestoneName": "原型设计", "sortOrder": 2, "status": "COMPLETED", "startTime": "2026-8-13 11:04:11", "endTime": "2026-8-13 11:04:11", "parallelFlag": false }, + { "id": "3", "projectId": "1", "milestoneName": "开发实现", "sortOrder": 3, "status": "IN_PROGRESS", "startTime": null, "endTime": null, "parallelFlag": false }, + { "id": "4", "projectId": "1", "milestoneName": "需求验收", "sortOrder": 4, "status": "UNSTARTED", "startTime": null, "endTime": null, "parallelFlag": false }, + { "id": "5", "projectId": "1", "milestoneName": "项目交付", "sortOrder": 5, "status": "UNSTARTED", "startTime": null, "endTime": null, "parallelFlag": false } + ], + "members": [ + { "id": "1", "projectId": "1", "userId": "1", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": true }, + { "id": "2", "projectId": "1", "userId": "2", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false }, + { "id": "3", "projectId": "1", "userId": "3", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false }, + { "id": "4", "projectId": "1", "userId": "4", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false }, + { "id": "5", "projectId": "1", "userId": "5", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false }, + { "id": "6", "projectId": "1", "userId": "6", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false }, + { "id": "7", "projectId": "1", "userId": "7", "userName": "张三", "roleKey": "pm", "roleName": "项目经理", "isOwner": false } + ] + } +} diff --git a/diag.js b/diag.js new file mode 100644 index 0000000..f1ff454 --- /dev/null +++ b/diag.js @@ -0,0 +1,19 @@ +// @ts-nocheck +// 诊断:点页签后输出祖先链几何,定位额外间距来源 +const TAB = Number(process.argv[2] || 2) +const TARGET = process.argv[3] || '.project-files__list' + +const js = `(async function(){ + const tabs = document.querySelectorAll(".project-detail__tab"); + if (${TAB} > 0 && tabs[${TAB} - 1]) { tabs[${TAB} - 1].click(); await new Promise(r => setTimeout(r, 900)); } + let el = document.querySelector(${JSON.stringify(TARGET)}); + const chain = []; + while (el && chain.length < 6) { + const r = el.getBoundingClientRect(); + const cs = getComputedStyle(el); + chain.push({ cls: (el.className || '').toString().slice(0, 40), y: +r.y.toFixed(1), h: +r.height.toFixed(1), mt: cs.marginTop, pt: cs.paddingTop, pb: cs.paddingBottom, gap: cs.gap, rowGap: cs.rowGap }); + el = el.parentElement; + } + return JSON.stringify(chain, null, 0); +})()` +console.log(js) diff --git a/measure.js b/measure.js new file mode 100644 index 0000000..c2049cd --- /dev/null +++ b/measure.js @@ -0,0 +1,27 @@ +// @ts-nocheck +// 临时几何量测:把要测的选择器写进 SEL,执行 `node measure.js ` 生成命令 +const SELS = [ + '.project-detail__breadcrumb', + '.project-info-card', + '.project-info-card__logo', + '.project-info-card__name', + '.project-info-card__meta', + '.project-info-card__status', + '.project-detail__tabs', + '.project-detail__tab', + '.project-progress__title', + '.project-progress__head', + '.project-progress__track', + '.project-progress__range', + '.project-milestones__title', + '.project-milestones__track', + '.project-milestones__node', + '.project-milestones__dot', + '.project-team__title', + '.project-team__list', + '.project-team__avatar', +] + +const arr = JSON.stringify(SELS).replace(/"/g, '\\"') +const js = `(function(){const o={};for(const s of ${JSON.stringify(SELS)}){const e=document.querySelector(s);if(!e){o[s]=null;continue}const r=e.getBoundingClientRect();o[s]=[+r.x.toFixed(1),+r.y.toFixed(1),+r.width.toFixed(1),+r.height.toFixed(1)]}return JSON.stringify(o)})()` +console.log(js) diff --git a/src/assets/home/banner-illustration.png b/src/assets/home/banner-illustration.png new file mode 100644 index 0000000..05a70be Binary files /dev/null and b/src/assets/home/banner-illustration.png differ diff --git a/src/assets/home/business-hardware.png b/src/assets/home/business-hardware.png new file mode 100644 index 0000000..6437465 Binary files /dev/null and b/src/assets/home/business-hardware.png differ diff --git a/src/assets/home/business-internet.png b/src/assets/home/business-internet.png new file mode 100644 index 0000000..744f7d5 Binary files /dev/null and b/src/assets/home/business-internet.png differ diff --git a/src/assets/home/business-iot.png b/src/assets/home/business-iot.png new file mode 100644 index 0000000..5ada0ba Binary files /dev/null and b/src/assets/home/business-iot.png differ diff --git a/src/assets/home/business-manufacturing.png b/src/assets/home/business-manufacturing.png new file mode 100644 index 0000000..be84580 Binary files /dev/null and b/src/assets/home/business-manufacturing.png differ diff --git a/src/assets/home/entry-cases.png b/src/assets/home/entry-cases.png new file mode 100644 index 0000000..947aacb Binary files /dev/null and b/src/assets/home/entry-cases.png differ diff --git a/src/assets/home/entry-contract.png b/src/assets/home/entry-contract.png new file mode 100644 index 0000000..82a66d4 Binary files /dev/null and b/src/assets/home/entry-contract.png differ diff --git a/src/assets/home/entry-demand.png b/src/assets/home/entry-demand.png new file mode 100644 index 0000000..58ad252 Binary files /dev/null and b/src/assets/home/entry-demand.png differ diff --git a/src/assets/home/entry-finance.png b/src/assets/home/entry-finance.png new file mode 100644 index 0000000..7e7320b Binary files /dev/null and b/src/assets/home/entry-finance.png differ diff --git a/src/assets/home/entry-service.png b/src/assets/home/entry-service.png new file mode 100644 index 0000000..30423b6 Binary files /dev/null and b/src/assets/home/entry-service.png differ diff --git a/src/components/AppFooter.vue b/src/components/AppFooter.vue index f3ad334..de22589 100644 --- a/src/components/AppFooter.vue +++ b/src/components/AppFooter.vue @@ -3,12 +3,13 @@ import { useSiteStore } from '@/stores/modules/site' const siteStore = useSiteStore() +// 设计稿快速导航列原文:首页 / 案例中心 / 需求提报 / 下载 APP / 帮助中心 const quickLinks = [ { label: '首页', path: '/' }, - { label: '案例库', path: '/cases' }, + { label: '案例中心', path: '/cases' }, { label: '需求提报', path: '/demand' }, { label: '下载 APP', path: '/' }, - { label: '消息中心', path: '/message' }, + { label: '帮助中心', path: '/profile/help' }, ] const coreBusiness = [ @@ -22,7 +23,7 @@ const coreBusiness = [ diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue index d55bd22..942b955 100644 --- a/src/components/AppHeader.vue +++ b/src/components/AppHeader.vue @@ -1,13 +1,11 @@ diff --git a/src/views/project/cpns/ProjectInfoCard.vue b/src/views/project/cpns/ProjectInfoCard.vue index 95f6f01..7707bb7 100644 --- a/src/views/project/cpns/ProjectInfoCard.vue +++ b/src/views/project/cpns/ProjectInfoCard.vue @@ -65,60 +65,58 @@ function businessTypeText(project: Project): string { diff --git a/src/views/project/cpns/ProjectProgress.vue b/src/views/project/cpns/ProjectProgress.vue index 94dfc14..52acace 100644 --- a/src/views/project/cpns/ProjectProgress.vue +++ b/src/views/project/cpns/ProjectProgress.vue @@ -44,45 +44,54 @@ function clampPercent(value: number): number { diff --git a/src/views/project/cpns/ProjectTeam.vue b/src/views/project/cpns/ProjectTeam.vue index 5ed8a2f..e1e676f 100644 --- a/src/views/project/cpns/ProjectTeam.vue +++ b/src/views/project/cpns/ProjectTeam.vue @@ -33,75 +33,80 @@ defineProps() diff --git a/src/views/project/detail.vue b/src/views/project/detail.vue index ca916b7..14fc56a 100644 --- a/src/views/project/detail.vue +++ b/src/views/project/detail.vue @@ -131,7 +131,9 @@ onMounted(load) diff --git a/tabs.js b/tabs.js new file mode 100644 index 0000000..d0287e0 --- /dev/null +++ b/tabs.js @@ -0,0 +1,36 @@ +// @ts-nocheck +// 逐页签量测:node tabs.js 生成一段 JS,在页面内点第 N 个页签后量测 +const TAB = Number(process.argv[2] || 0) +const SELS = process.argv[3] + ? process.argv[3].split(',') + : [ + '.project-detail__tabs', + '.project-files__list', + '.project-files__item', + '.project-finance__title', + '.project-finance__plans', + '.project-finance__plan', + '.project-finance__files', + '.project-finance__file', + '.project-after-sales__banner', + '.project-after-sales__title', + '.project-after-sales__field', + '.project-after-sales__readonly', + '.project-after-sales__actions', + '.project-after-sales__btn', + ] + +const js = `(async function(){ + const tabs = document.querySelectorAll(".project-detail__tab"); + if (${TAB} > 0 && tabs[${TAB} - 1]) { tabs[${TAB} - 1].click(); await new Promise(r => setTimeout(r, 900)); } + const o = {}; + for (const s of ${JSON.stringify(SELS)}) { + const list = document.querySelectorAll(s); + o[s] = list.length === 0 ? null : Array.from(list).slice(0, 3).map(e => { + const r = e.getBoundingClientRect(); + return [+r.x.toFixed(1), +r.y.toFixed(1), +r.width.toFixed(1), +r.height.toFixed(1)]; + }); + } + return JSON.stringify(o); +})()` +console.log(js) diff --git a/vite.visual.config.ts b/vite.visual.config.ts new file mode 100644 index 0000000..4b7493d --- /dev/null +++ b/vite.visual.config.ts @@ -0,0 +1,110 @@ +// 视觉走查专用:在 vite.config.ts 之上叠加 mock 中间件(仅本地临时使用,不参与构建) +import { fileURLToPath, URL } from 'node:url' +import { readFileSync, existsSync } from 'node:fs' +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +const MOCK_DIR = fileURLToPath(new URL('./.mock', import.meta.url)) + +/** 把 mock 目录下的 json 按路径映射回响应 */ +const ROUTES: Array<[RegExp, string]> = [ + [/\/web\/requirementPool\/get\/progress\//, 'progress.json'], + [/\/web\/requirementPool\/get\/files\//, 'files.json'], + [/\/web\/requirementPool\/get\/finance\//, 'finance.json'], + [/\/web\/requirementPool\/get\/afterSales\//, 'afterSales.json'], + [/\/web\/bill\/list/, 'bills.json'], +] + +/** + * 状态变体覆盖(走查专用)。 + * + *

同一张设计稿的多个状态(如「售后信息」只看已完成 + 处理中)无法靠一份静态 + * json 同时表达,所以支持 `?state=done` 之类的开关在服务端就地改写字段, + * 免得为了看一个状态去反复改 mock 文件。

+ * + *

注意:详情页首屏 4 个接口同帧并发,顶部信息卡的 `status` 取**任意一支**先返回 + * 的,所以只改一支会出现「头说进行中、体说已完成」的错位。变体必须 4 支都打。

+ */ +const VARIANTS: Record> = { + // 项目已完成 + 有处理中的售后工单 → 对应设计稿 ef0166c6 + done: [['status', 'COMPLETED']], +} + +/** 按点号路径写值,创建不存在的中间层 */ +function setPath(obj: Record, path: string, value: unknown): void { + const keys = path.split('.') + let cur = obj + for (let i = 0; i < keys.length - 1; i += 1) { + const k = keys[i] + if (typeof cur[k] !== 'object' || cur[k] === null) cur[k] = {} + cur = cur[k] as Record + } + cur[keys[keys.length - 1]] = value +} + +export default defineConfig({ + plugins: [ + vue(), + { + name: 'visual-mock', + configureServer(server) { + server.middlewares.use((req, res, next) => { + const url = req.url ?? '' + // 状态开关从 **Referer** 取(= 当前页面 URL),而不是 req.url: + // 业务代码发的接口地址里没有页面 query,走查期又不想为看一个状态改应用代码。 + const referer = String(req.headers.referer ?? '') + const state = referer ? new URL(referer).searchParams.get('state') : null + for (const [re, file] of ROUTES) { + if (!re.test(url)) continue + const path = `${MOCK_DIR}/${file}` + if (!existsSync(path)) break + + let body = readFileSync(path, 'utf-8') + const patch = state ? VARIANTS[state] : undefined + if (patch) { + const json = JSON.parse(body) + for (const [p, v] of patch) setPath(json, `data.${p}`, v) + body = JSON.stringify(json) + } + + res.setHeader('Content-Type', 'application/json; charset=utf-8') + res.end(body) + return + } + next() + }) + }, + }, + ], + server: { + proxy: { + // 走查期不转发后端:未命中的接口直接 404(避免真实后端的 401 干扰观感) + '/api': { + target: 'http://127.0.0.1:1', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + configure: (proxy) => { + proxy.on('error', (_err, _req, res) => { + if (res && !res.headersSent) { + res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }) + } + res?.end?.(JSON.stringify({ code: 200, msg: 'ok', data: null })) + }) + }, + }, + }, + }, + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + css: { + preprocessorOptions: { + less: { + additionalData: + '@import "@/styles/variables.less";\n@import "@/styles/mixins.less";', + }, + }, + }, +})