diff --git a/web-admin/scripts/test-mp-crm-customer-confirm-dom.mjs b/web-admin/scripts/test-mp-crm-customer-confirm-dom.mjs new file mode 100644 index 000000000..aa896d5ea --- /dev/null +++ b/web-admin/scripts/test-mp-crm-customer-confirm-dom.mjs @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' + +const root = new URL('../src/views/m/crm/customer/', import.meta.url) +const files = [ + 'index.vue', + 'detail.vue', + 'form.vue', + 'ContactPanel.vue', + 'FollowPanel.vue', + 'InvoicePanel.vue', + 'OpportunityActions.vue', + 'CustomerPoolSheet.vue' +] + +const sources = {} +for (const name of files) { + sources[name] = await readFile(new URL(name, root), 'utf8') +} + +const customerScss = await readFile(new URL('customer.scss', root), 'utf8') +assert.match( + customerScss, + /van-dialog:not\(\.mp-crm-dialog\)/, + '客户 scss 不得用旧 Dialog 按钮样式压扁 mp-crm-dialog' +) + +for (const [name, src] of Object.entries(sources)) { + assert.doesNotMatch(src, /showConfirmDialog\s*\(/, `${name} 不得直接调用 showConfirmDialog`) + assert.match(src, /showCrmConfirmDialog|from '@\/utils\/mp-crm-confirm'/, `${name} 必须使用 CRM 确认弹窗`) +} + +assert.match(sources['index.vue'], /kind:\s*'danger'/, '列表删除客户须 danger') +assert.match(sources['detail.vue'], /kind:\s*'danger'/, '详情删除客户须 danger') +assert.match(sources['ContactPanel.vue'], /kind:\s*'danger'/, '删除联系人须 danger') +assert.match(sources['ContactPanel.vue'], /kind:\s*'default'/, '设主要联系人须 default') +assert.match(sources['FollowPanel.vue'], /kind:\s*'danger'/, '删除跟进须 danger') +assert.match(sources['InvoicePanel.vue'], /kind:\s*'danger'/, '删除开票须 danger') +assert.match(sources['OpportunityActions.vue'], /kind:\s*'danger'/, '删除商机须 danger') +assert.match(sources['form.vue'], /kind:\s*'default'/, '新增字典项须 default') +assert.match(sources['CustomerPoolSheet.vue'], /kind:\s*'default'/, '放入公海须 default') + +console.log('mp-crm-customer-confirm-dom: ok') diff --git a/web-admin/scripts/test-mp-crm-product-confirm-dom.mjs b/web-admin/scripts/test-mp-crm-product-confirm-dom.mjs index 01b745781..e70ef5aac 100644 --- a/web-admin/scripts/test-mp-crm-product-confirm-dom.mjs +++ b/web-admin/scripts/test-mp-crm-product-confirm-dom.mjs @@ -2,27 +2,29 @@ import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' const root = new URL('../src/views/m/crm/product/', import.meta.url) -const util = await readFile(new URL('../src/utils/mp-crm-product-confirm.ts', import.meta.url), 'utf8') -const scss = await readFile(new URL('product.scss', root), 'utf8') +const util = await readFile(new URL('../src/utils/mp-crm-confirm.ts', import.meta.url), 'utf8') +const alias = await readFile(new URL('../src/utils/mp-crm-product-confirm.ts', import.meta.url), 'utf8') +const primitives = await readFile(new URL('../src/styles/mp-crm-primitives.scss', import.meta.url), 'utf8') const list = await readFile(new URL('index.vue', root), 'utf8') const detail = await readFile(new URL('detail.vue', root), 'utf8') const categories = await readFile(new URL('categories.vue', root), 'utf8') -assert.match(util, /ProductConfirmKind\s*=\s*'default'\s*\|\s*'danger'/, 'helper 必须支持 danger kind') +assert.match(util, /CrmConfirmKind\s*=\s*'default'\s*\|\s*'danger'/, 'helper 必须支持 danger kind') assert.match(util, /kind\s*===\s*'danger'/, 'helper 必须分支处理 danger') -assert.match(util, /mp-product-dialog--danger/, 'helper 必须带 danger className') -assert.match(util, /mp-product-dialog__title/, '方案 B 标题须写入 HTML(图标在上)') +assert.match(util, /mp-crm-dialog--danger/, 'helper 必须带 danger className') +assert.match(util, /mp-crm-dialog__title/, '方案 B 标题须写入 HTML') assert.match(util, /#dc2626/, '删除确认色必须为危险红') assert.match(util, /#0a9f6d/, '普通确认色必须为三墙绿') assert.doesNotMatch(util, /round-button/, '禁止 round-button 主题') assert.doesNotMatch(util, /#10b981/, '禁止 Fresh Mint') -assert.match(scss, /body \.van-dialog\.mp-product-dialog/, 'scss 必须限定产品 Dialog') -assert.match(scss, /mp-product-dialog--danger/, 'scss 必须覆盖危险确认键') -assert.match(scss, /mp-product-dialog__warn-icon/, 'scss 必须有警示图标') -assert.match(scss, /mp-product-dialog__title/, 'scss 必须有居中标题') -assert.match(scss, /text-align:\s*center/, '标题/文案须居中') -assert.doesNotMatch(scss, /#10b981/, 'scss 禁止 Fresh Mint') +assert.match(alias, /showCrmConfirmDialog as showProductConfirmDialog/, '产品侧须转发共用 helper') + +assert.match(primitives, /body \.van-dialog\.mp-crm-dialog/, 'primitives 必须限定 CRM Dialog') +assert.match(primitives, /mp-crm-dialog--danger/, 'primitives 必须覆盖危险确认键') +assert.match(primitives, /mp-crm-dialog__warn-icon/, 'primitives 必须有警示图标') +assert.match(primitives, /mp-crm-dialog__title/, 'primitives 必须有居中标题') +assert.doesNotMatch(primitives, /#10b981/, 'scss 禁止 Fresh Mint') for (const [name, src] of [ ['index', list], diff --git a/web-admin/src/styles/mp-crm-primitives.scss b/web-admin/src/styles/mp-crm-primitives.scss new file mode 100644 index 000000000..807cb2e8b --- /dev/null +++ b/web-admin/src/styles/mp-crm-primitives.scss @@ -0,0 +1,532 @@ +/* 工作台业务页视觉原语:仅通过局部 modifier 启用,避免改变历史两栏筛选消费者。 */ +.mp-wv-touch-target { + min-width: 44px; + min-height: 44px; +} + +.mp-wv-status-tabs { + display: flex; + gap: 8px; + padding: 0 0 10px; + overflow-x: auto; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} + +.mp-wv-status-tabs::-webkit-scrollbar { + display: none; +} + +.mp-wv-status-tabs .filter-tag { + flex: 0 0 auto; + min-height: 44px; + padding: 0 14px; + font-size: 13px; + color: #6b7b74; + background: #fff; + border: 1px solid #dcebe4; + border-radius: 999px; +} + +.mp-wv-status-tabs .filter-tag.is-active { + font-weight: 600; + color: #087f57; + background: #e9f9f2; + border-color: #0a9f6d; +} + +.mp-wv-status-tabs .filter-tag:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +.mp-wv-status-tabs .filter-tag__count { + margin-left: 4px; + font-size: 11px; +} + +.mp-wv-filter-wrap.mp-wv-filter-wrap--business { + z-index: 21; +} + +.mp-wv-filter-bar.mp-wv-filter-bar--three { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.mp-wv-filter-bar--three .mp-wv-filter-trig { + min-height: 44px; +} + +.mp-wv-filter-wrap--business .mp-wv-filter-opt { + min-height: 44px; +} + +.mp-wv-filter-wrap--business > .mp-wv-filter-mask { + z-index: 7; +} + +.mp-wv-filter-more__dot { + position: absolute; + top: 9px; + right: calc(50% - 24px); + width: 7px; + height: 7px; + background: #dc2626; + border: 1px solid #fff; + border-radius: 50%; +} + +.mp-crm-expense-card { + margin-bottom: 10px; + overflow: hidden; + background: #fff; + border: 1px solid #dcebe4; + border-radius: 14px; + box-shadow: 0 6px 16px rgb(10 159 109 / 8%); +} + +.mp-crm-expense-card .ec-top { + display: flex; + gap: 8px; + align-items: center; + padding: 12px 12px 8px; +} + +.mp-crm-expense-card .expense-card__select { + display: inline-flex; + flex: 0 0 44px; + align-items: center; + justify-content: center; + padding: 0; + background: transparent; + border: 0; +} + +.mp-crm-expense-card .expense-card__select > span { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + color: #fff; + background: #0a9f6d; + border: 1px solid #0a9f6d; + border-radius: 50%; +} + +.mp-crm-expense-card .expense-card__select[aria-pressed='false'] > span { + background: #fff; + border-color: #a8bbb2; +} + +.mp-crm-expense-card .ec-title { + flex: 1; + min-width: 0; + margin: 0; + overflow: hidden; + font-size: 15px; + font-weight: 700; + color: #1f2a26; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mp-crm-expense-card .expense-card__open { + display: block; + width: 100%; + padding: 0; + overflow: hidden; + font: inherit; + color: inherit; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + background: transparent; + border: 0; +} + +.mp-crm-expense-card .status-tag, +.mp-crm-approval-timeline .status-tag { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + min-height: 24px; + padding: 2px 8px; + font-size: 11px; + font-weight: 600; + line-height: 1.2; + border: 1px solid transparent; + border-radius: 999px; +} + +.mp-crm-expense-card .status-tag--success, +.mp-crm-approval-timeline .status-tag--success { + color: #087f57; + background: #e9f9f2; + border-color: #b7ebce; +} + +.mp-crm-expense-card .status-tag--primary, +.mp-crm-approval-timeline .status-tag--primary { + color: #087f57; + background: #e9f9f2; + border-color: #b7ebce; +} + +.mp-crm-expense-card .status-tag--warning, +.mp-crm-approval-timeline .status-tag--warning { + color: #a16207; + background: #fffbeb; + border-color: #fde68a; +} + +.mp-crm-expense-card .status-tag--danger, +.mp-crm-approval-timeline .status-tag--danger { + color: #b91c1c; + background: #fef2f2; + border-color: #fecaca; +} + +.mp-crm-expense-card .status-tag--info, +.mp-crm-approval-timeline .status-tag--info { + color: #475569; + background: #f1f5f9; + border-color: #dbe4ee; +} + +.mp-crm-expense-card .ec-amount { + padding: 4px 12px 10px; +} + +.mp-crm-expense-card .ec-amount__label { + display: block; + margin-bottom: 2px; + font-size: 11px; + color: #6b7b74; +} + +.mp-crm-expense-card .ec-amount__value { + font-size: 24px; + line-height: 1.2; + color: #087f57; +} + +.mp-crm-expense-card .ec-emphasis { + display: flex; + gap: 10px; + align-items: center; + justify-content: space-between; + padding: 7px 12px; + font-size: 13px; + color: #334155; + border-top: 1px solid #f0f4f2; +} + +.mp-crm-expense-card .list-card-kv { + padding: 8px 10px; + margin: 0 12px 8px; + font-size: 12px; + line-height: 1.55; + color: #475569; + background: #f7faf8; + border-radius: 9px; +} + +.mp-crm-expense-card .ec-time { + padding: 0 12px 10px; + margin: 0; + font-size: 11px; + color: #94a3b8; +} + +.mp-crm-expense-card .ec-bottom { + padding: 8px 12px; + border-top: 1px dashed #cbded4; +} + +.mp-crm-expense-card .op-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: flex-end; +} + +.mp-crm-expense-card .op-actions > button, +.mp-crm-expense-card .op-actions > a { + min-height: 44px; +} + +.mp-crm-att-grid { + display: grid; + grid-template-columns: repeat(auto-fill, 72px); + gap: 10px; + padding: 0; + margin: 0; + list-style: none; +} + +.mp-crm-att-grid .att-item, +.mp-crm-att-grid .att-img, +.mp-crm-att-grid .att-doc-thumb, +.mp-crm-att-grid .att-add { + width: 72px; + height: 72px; +} + +.mp-crm-att-grid .att-item { + position: relative; +} + +.mp-crm-att-grid .att-preview, +.mp-crm-att-grid .att-add { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + overflow: hidden; + background: #f7faf8; + border: 1px solid #dcebe4; + border-radius: 8px; +} + +.mp-crm-att-grid .att-img { + display: block; + object-fit: cover; +} + +.mp-crm-att-grid .att-doc-thumb { + display: flex; + flex-direction: column; + gap: 4px; + align-items: center; + justify-content: center; + padding: 6px; + color: #087f57; + text-align: center; +} + +.mp-crm-att-grid .att-doc-thumb small { + width: 100%; + overflow: hidden; + font-size: 10px; + color: #6b7b74; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mp-crm-att-grid .att-add { + flex-direction: column; + gap: 2px; + color: #087f57; + background: #fff; + border-color: #8ed8b8; + border-style: dashed; +} + +.mp-crm-att-grid .att-add > span { + font-size: 24px; + line-height: 1; +} + +.mp-crm-att-grid .att-remove { + position: absolute; + top: -11px; + right: -11px; + z-index: 1; + display: inline-flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + padding: 0; + font-size: 16px; + line-height: 1; + color: #fff; + background: radial-gradient(circle, #dc2626 0 10px, transparent 11px); + border: 0; +} + +.mp-crm-approval-timeline { + padding: 0; + margin: 0; + list-style: none; +} + +.mp-crm-approval-timeline__item { + display: grid; + grid-template-columns: 38px minmax(0, 1fr); + gap: 10px; + min-height: 64px; +} + +.mp-crm-approval-timeline__rail { + position: relative; + display: flex; + justify-content: center; +} + +.mp-crm-approval-timeline__rail::after { + position: absolute; + top: 34px; + bottom: 0; + width: 1px; + content: ''; + background: #cfe7dc; +} + +.mp-crm-approval-timeline__item:last-child .mp-crm-approval-timeline__rail::after { + display: none; +} + +.mp-crm-approval-timeline__avatar { + z-index: 1; + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + font-size: 13px; + font-weight: 700; + color: #fff; + background: #0a9f6d; + border-radius: 50%; +} + +.mp-crm-approval-timeline__content { + padding: 0 0 16px; +} + +.mp-crm-approval-timeline__head { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-height: 32px; + font-size: 13px; +} + +.mp-crm-approval-timeline__opinion, +.mp-crm-approval-timeline__time { + display: block; + margin: 4px 0 0; + font-size: 12px; + line-height: 1.45; + color: #6b7b74; +} + +/* ── CRM 确认弹窗:方案 A(绿)/ B(红+警示),className 限定 ── */ +body .van-dialog.mp-crm-dialog { + overflow: hidden; + width: 300px !important; + max-width: calc(100vw - 48px); + border-radius: 16px; + box-shadow: 0 16px 40px rgb(15 40 30 / 22%); +} + +body .van-dialog.mp-crm-dialog .van-dialog__header { + padding: 20px 20px 0; + font-size: 16px; + font-weight: 700; + color: #1f2a26; + line-height: 1.4; + text-align: center; +} + +body .van-dialog.mp-crm-dialog .van-dialog__content { + padding: 0; +} + +body .van-dialog.mp-crm-dialog .van-dialog__message { + padding: 10px 20px 20px; + font-size: 14px; + line-height: 1.55; + color: #6b7b74; + text-align: center; +} + +body .van-dialog.mp-crm-dialog.mp-crm-dialog--danger .van-dialog__header { + display: none; +} + +body .van-dialog.mp-crm-dialog.mp-crm-dialog--danger .van-dialog__message { + padding: 20px 20px 20px; +} + +body .van-dialog.mp-crm-dialog .mp-crm-dialog__warn { + display: flex; + justify-content: center; + margin: 0 0 12px; +} + +body .van-dialog.mp-crm-dialog .mp-crm-dialog__warn-icon { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + font-size: 22px; + font-weight: 700; + line-height: 1; + color: #dc2626; + background: #fef2f2; + border-radius: 50%; +} + +body .van-dialog.mp-crm-dialog .mp-crm-dialog__title { + margin: 0 0 8px; + font-size: 16px; + font-weight: 700; + line-height: 1.4; + color: #1f2a26; + text-align: center; +} + +body .van-dialog.mp-crm-dialog .mp-crm-dialog__msg { + color: #6b7b74; + text-align: center; +} + +body .van-dialog.mp-crm-dialog .van-dialog__footer { + display: flex; + gap: 10px; + padding: 0 16px 16px; + border-top: none !important; +} + +body .van-dialog.mp-crm-dialog .van-dialog__footer::after, +body .van-dialog.mp-crm-dialog .van-hairline--left::after, +body .van-dialog.mp-crm-dialog .van-hairline--top::after { + display: none !important; +} + +body .van-dialog.mp-crm-dialog .van-dialog__cancel, +body .van-dialog.mp-crm-dialog .van-dialog__confirm { + flex: 1; + height: 40px !important; + min-height: 40px !important; + margin: 0 !important; + padding: 0 10px !important; + font-size: 14px !important; + font-weight: 600 !important; + line-height: 40px !important; + border-radius: 10px !important; + border: 1px solid #dcebe4 !important; +} + +body .van-dialog.mp-crm-dialog .van-dialog__cancel { + color: #1f2a26 !important; + background: #fff !important; +} + +body .van-dialog.mp-crm-dialog .van-dialog__confirm { + color: #fff !important; + background: #0a9f6d !important; + border-color: #0a9f6d !important; +} + +body .van-dialog.mp-crm-dialog.mp-crm-dialog--danger .van-dialog__confirm { + background: #dc2626 !important; + border-color: #dc2626 !important; +} diff --git a/web-admin/src/utils/mp-crm-confirm.test.ts b/web-admin/src/utils/mp-crm-confirm.test.ts new file mode 100644 index 000000000..7e9e491dc --- /dev/null +++ b/web-admin/src/utils/mp-crm-confirm.test.ts @@ -0,0 +1,49 @@ +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { buildCrmConfirmOptions } from './mp-crm-confirm' + +describe('buildCrmConfirmOptions', () => { + it('builds scheme A for non-delete confirms', () => { + const opts = buildCrmConfirmOptions({ + kind: 'default', + title: '放入公海', + message: '确定将客户放入公海吗?' + }) + assert.equal(opts.title, '放入公海') + assert.equal(opts.message, '确定将客户放入公海吗?') + assert.equal(opts.allowHtml, false) + assert.equal(opts.className, 'mp-crm-dialog') + assert.equal(opts.confirmButtonText, '确认') + assert.equal(opts.confirmButtonColor, '#0a9f6d') + assert.equal(opts.theme, undefined) + }) + + it('builds scheme B for delete confirms with warn html', () => { + const opts = buildCrmConfirmOptions({ + kind: 'danger', + title: '删除客户', + message: '确认删除该客户吗?' + }) + assert.equal(opts.title, '') + assert.equal(opts.allowHtml, true) + assert.equal(opts.className, 'mp-crm-dialog mp-crm-dialog--danger') + assert.equal(opts.confirmButtonText, '确认删除') + assert.equal(opts.confirmButtonColor, '#dc2626') + assert.match(String(opts.message), /mp-crm-dialog__warn/) + assert.match(String(opts.message), /mp-crm-dialog__title/) + assert.match(String(opts.message), /删除客户/) + assert.match(String(opts.message), /确认删除该客户吗?/) + assert.doesNotMatch(String(opts.message), / diff --git a/web-admin/src/views/m/crm/customer/CustomerPoolSheet.vue b/web-admin/src/views/m/crm/customer/CustomerPoolSheet.vue new file mode 100644 index 000000000..576d63cb2 --- /dev/null +++ b/web-admin/src/views/m/crm/customer/CustomerPoolSheet.vue @@ -0,0 +1,94 @@ + + + diff --git a/web-admin/src/views/m/crm/customer/FollowPanel.vue b/web-admin/src/views/m/crm/customer/FollowPanel.vue new file mode 100644 index 000000000..fe23ac2e2 --- /dev/null +++ b/web-admin/src/views/m/crm/customer/FollowPanel.vue @@ -0,0 +1,280 @@ + + diff --git a/web-admin/src/views/m/crm/customer/InvoicePanel.vue b/web-admin/src/views/m/crm/customer/InvoicePanel.vue new file mode 100644 index 000000000..f05d75b7d --- /dev/null +++ b/web-admin/src/views/m/crm/customer/InvoicePanel.vue @@ -0,0 +1,373 @@ + + diff --git a/web-admin/src/views/m/crm/customer/OpportunityActions.vue b/web-admin/src/views/m/crm/customer/OpportunityActions.vue new file mode 100644 index 000000000..64477caab --- /dev/null +++ b/web-admin/src/views/m/crm/customer/OpportunityActions.vue @@ -0,0 +1,326 @@ + + diff --git a/web-admin/src/views/m/crm/customer/customer.scss b/web-admin/src/views/m/crm/customer/customer.scss new file mode 100644 index 000000000..7cf0222ad --- /dev/null +++ b/web-admin/src/views/m/crm/customer/customer.scss @@ -0,0 +1,991 @@ +/* + * Vant 下拉/弹层会 teleport 到 body,页面内 CSS 变量传不进去。 + * 必须在 :root 覆盖主色,否则选项高亮仍是默认蓝 #1989fa。 + */ +:root { + --van-primary-color: #0a9f6d; + --van-success-color: #0a9f6d; + --van-danger-color: #dc2626; + --van-warning-color: #d97706; + --van-button-primary-background: #0a9f6d; + --van-button-primary-border-color: #0a9f6d; + --van-dropdown-menu-title-active-text-color: #0a9f6d; + --van-dropdown-menu-option-active-color: #0a9f6d; + --van-tabs-bottom-bar-color: #0a9f6d; + --van-tab-active-text-color: #0a9f6d; + --van-nav-bar-icon-color: #0a9f6d; + --van-nav-bar-text-color: #0a9f6d; + --van-checkbox-checked-icon-color: #0a9f6d; + --van-switch-on-background: #0a9f6d; + --van-radio-checked-icon-color: #0a9f6d; + --van-picker-confirm-action-color: #0a9f6d; + --van-dialog-confirm-button-text-color: #0a9f6d; + --van-loading-spinner-color: #0a9f6d; + --van-tag-primary-color: #0a9f6d; +} + +.mp-customer-page, +.mp-customer-sheet, +.mp-customer-filter-vant, +.mp-customer-pool-vant, +.mp-customer-vant-popup { + /* 页面内组件再写一遍,保证未 teleport 的控件也一定吃到三墙绿 */ + --van-primary-color: #0a9f6d; + --van-success-color: #0a9f6d; + --van-danger-color: #dc2626; + --van-warning-color: #d97706; + --van-button-primary-background: #0a9f6d; + --van-button-primary-border-color: #0a9f6d; + --van-nav-bar-icon-color: #0a9f6d; + --van-nav-bar-text-color: #0a9f6d; + --van-tabs-bottom-bar-color: #0a9f6d; + --van-tab-active-text-color: #0a9f6d; + --van-dropdown-menu-title-active-text-color: #0a9f6d; + --van-dropdown-menu-option-active-color: #0a9f6d; + --van-checkbox-checked-icon-color: #0a9f6d; + --van-switch-on-background: #0a9f6d; + --van-radio-checked-icon-color: #0a9f6d; + --van-slider-active-background: #0a9f6d; + --van-tag-primary-color: #0a9f6d; + --van-loading-spinner-color: #0a9f6d; + --van-picker-confirm-action-color: #0a9f6d; + + font-size: 14px; + + button, + summary { + min-width: 44px; + min-height: 44px; + } + + button { + cursor: pointer; + } + + .mp-customer-required, + .mp-customer-error { + color: #b42318; + } + + .mp-customer-error { + padding: 12px; + background: #fff2f0; + border-radius: 10px; + } + + .mp-customer-value { + overflow-wrap: anywhere; + white-space: pre-wrap; + } + + .mp-customer-paste { + margin-bottom: 16px; + + .mp-customer-paste__actions { + display: flex; + justify-content: flex-end; + margin-top: 12px; + padding: 0 16px 4px; + } + + .mp-customer-paste__hint { + margin: 8px 16px 0; + font-size: 12px; + line-height: 1.4; + color: #6b7b74; + } + } + + .mp-customer-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + margin: 10px 0; + } + + /* 详情 Tab:整块透明,透出页面灰底 */ + .mp-customer-detail-tabs { + margin: 0 -10px 12px; + background: transparent !important; + + --van-tabs-line-height: 44px; + --van-tabs-nav-background: transparent; + --van-tab-text-color: #6b7b74; + --van-tab-active-text-color: #087f57; + --van-tab-font-size: 13px; + --van-tabs-bottom-bar-color: #0a9f6d; + --van-tabs-bottom-bar-height: 2px; + --van-cell-background: transparent; + --van-cell-group-background: transparent; + /* Vant 默认 sticky z=99,会盖过绿头(50);压到绿头之下 */ + --van-sticky-z-index: 40; + + /* 吸顶时仍低于绿头(z=50),并铺实色避免正文透出 */ + .van-sticky, + .van-sticky--fixed { + z-index: 40 !important; + } + + .van-tabs__wrap { + background: #f5f8f7 !important; + border-bottom: 1px solid #dcebe4; + box-shadow: none; + } + + .van-tabs__nav { + padding: 0 6px; + background: #f5f8f7 !important; + } + + .van-tab { + flex: 0 0 auto; + min-width: 72px; + padding: 0 14px; + font-size: 13px; + font-weight: 600; + color: #6b7b74; + } + + .van-tab--active { + font-weight: 700; + color: #087f57; + } + + .van-tabs__line { + bottom: 2px; + width: 28px !important; + height: 2px !important; + background: #0a9f6d !important; + border-radius: 2px; + } + + .van-tabs__content, + .van-tabs__track, + .van-tab__panel { + overflow: visible !important; + background: transparent !important; + } + + /* 详情正文:去掉白底区块 / 字段组 */ + .mp-customer-content { + padding-right: 10px; + padding-left: 10px; + background: transparent !important; + + .mp-wv-section { + padding: 4px 0 12px; + background: transparent !important; + border: 0 !important; + border-radius: 0; + box-shadow: none; + } + + .mp-customer-fields-vant, + .mp-customer-fields-vant.van-cell-group, + .mp-customer-fields-vant .van-cell { + background: transparent !important; + } + + .mp-customer-fields-vant::after, + .mp-customer-fields-vant.van-hairline--top-bottom::after { + display: none !important; + } + + /* 跟进/联系人等单条记录卡仍保留白底 */ + .mp-customer-panel-card.van-cell-group, + .mp-customer-panel-card .van-cell { + background: #fff !important; + } + } + } + + .mp-customer-panel-card { + margin-bottom: 10px; + overflow: hidden; + + .mp-customer-tags, + .mp-customer-value, + .mp-customer-actions, + .mp-wv-card__meta, + .mp-customer-attachments, + p { + padding-left: 16px; + padding-right: 16px; + } + + .mp-customer-actions { + padding-bottom: 12px; + } + } + + .mp-customer-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin: 8px 0; + } + + .mp-customer-content { + padding-bottom: calc(24px + env(safe-area-inset-bottom)); + } + + /* 仅「资料」Tab 有底部操作栏,需预留高度 */ + .mp-customer-content--fab { + padding-bottom: calc(90px + env(safe-area-inset-bottom)); + } + + .mp-customer-attachments { + width: 100%; + min-width: 0; + } + + .mp-customer-file-input { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); + } + + .mp-customer-attachment-hint { + margin: 8px 0 0; + font-size: 12px; + color: #6b7b74; + } + + .mp-customer-pre { + font-size: 12px; + overflow-wrap: anywhere; + white-space: pre-wrap; + } + + .mp-customer-loading { + display: flex; + justify-content: center; + padding: 48px 16px; + } + + .mp-customer-load-hint { + display: flex; + gap: 8px; + align-items: center; + justify-content: center; + margin: 12px 0 24px; + font-size: 13px; + color: #6b7b74; + } + + .mp-customer-load-sentinel { + height: 1px; + } + + .mp-customer-card-title { + display: block; + width: 100%; + padding: 0; + margin: 0 0 8px; + font: inherit; + font-weight: 600; + color: #0a9f6d; + text-align: left; + background: none; + border: 0; + cursor: pointer; + } +} + +.mp-customer-dd { + --van-dropdown-menu-height: 36px; + --van-dropdown-menu-background: transparent; + --van-dropdown-menu-shadow: none; + --van-dropdown-menu-title-font-size: 13px; + --van-dropdown-menu-option-active-color: #0a9f6d; + --van-primary-color: #0a9f6d; + margin: 0; + background: transparent; + border: 0; + box-shadow: none; + + .van-dropdown-menu__bar { + background: transparent; + box-shadow: none; + } +} + +.mp-customer-list .mp-wv-header { + padding-bottom: 10px; +} + +.mp-customer-list .mp-wv-header .mp-wv-search { + margin-bottom: 0; +} + +.mp-customer-list .mp-wv-header .mp-customer-dd { + margin-top: 4px; +} + +/* teleport 到 body 的下拉选项:直接兜底选中色 */ +.van-dropdown-item__option--active, +.van-dropdown-item__option--active .van-dropdown-item__icon { + color: #0a9f6d !important; +} + +.mp-customer-batch-bar { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 55; + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); + background: #fff; + border-top: 1px solid #dcebe4; + box-shadow: 0 -8px 24px rgb(34 64 52 / 12%); +} + +.mp-customer-batch-bar > span { + flex: 1 0 100%; + color: #6b7b74; +} + +.mp-customer-page .mp-wv-fab-bar { + /* 壳层 overflow-x: clip 会破坏 sticky;表单/详情底栏固定贴视口底 */ + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 40; + display: flex; + flex-wrap: nowrap; + gap: 8px; + justify-content: stretch; + width: 100%; + max-width: min(480px, 100%); + margin: 0 auto; + padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px)); + background: #fff; + border-top: 1px solid #dcebe4; + box-shadow: 0 -8px 24px rgb(34 64 52 / 12%); + + .van-button { + flex: 1 1 0; + min-width: 0; + } +} + +.mp-customer-page .mp-wv-speed-main { + width: 52px !important; + height: 52px !important; + min-width: 52px; + padding: 0; +} + +.mp-customer-sheet { + padding: 8px 0 16px; +} + +.mp-customer-filter-vant, +.mp-customer-pool-vant { + display: flex; + flex-direction: column; + box-sizing: border-box; + height: 100%; + min-height: 0; + background: #f7faf8; +} + +.mp-customer-filter-vant__body, +.mp-customer-pool-vant__body { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + -webkit-overflow-scrolling: touch; + padding: 8px 0 16px; +} + +/* 弹层内表单字段保持白底,避免透明后「空白」 */ +.mp-customer-pool-vant .mp-customer-fields-vant, +.mp-customer-vant-popup .mp-customer-fields-vant { + margin: 0 12px; + overflow: hidden; + background: #fff !important; + border-radius: 12px; + + .van-cell { + background: #fff !important; + } +} + +/* 详情 Tab 内列表卡:扁平结构,去掉强调条/KV 色块 */ +.mp-customer-detail-card { + cursor: default; + padding: 14px 14px 12px; + margin-bottom: 10px; + + .mp-customer-detail-card__head { + display: flex; + gap: 8px; + align-items: flex-start; + justify-content: space-between; + } + + .mp-customer-detail-card__main { + flex: 1; + min-width: 0; + } + + .mp-customer-detail-card__title { + font-size: 15px; + font-weight: 700; + color: #1f2a26; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .mp-customer-detail-card__meta { + margin-top: 4px; + font-size: 12px; + line-height: 1.45; + color: #6b7b74; + } + + .mp-customer-detail-card__toggle { + display: flex; + gap: 4px; + align-items: center; + justify-content: center; + width: 100%; + margin-top: 10px; + padding: 8px 0 2px; + font-size: 13px; + font-weight: 600; + color: #087f57; + background: none; + border: 0; + border-top: 1px dashed #dcebe4; + cursor: pointer; + } + + .mp-customer-detail-card__toggle-icon { + font-size: 11px; + line-height: 1; + } + + .mp-customer-detail-card__extra { + margin-top: 8px; + } + + .mp-customer-detail-card__body { + margin: 0 0 8px; + font-size: 14px; + line-height: 1.55; + color: #1f2a26; + white-space: pre-wrap; + word-break: break-word; + } + + .mp-customer-detail-card__rows { + margin-top: 0; + } + + .mp-customer-detail-card__row { + display: flex; + gap: 12px; + align-items: flex-start; + padding: 4px 0; + font-size: 13px; + line-height: 1.45; + + > span:first-child { + flex: 0 0 auto; + min-width: 5em; + color: #6b7b74; + white-space: nowrap; + } + + > span:last-child { + flex: 1; + min-width: 0; + color: #1f2a26; + text-align: right; + word-break: break-word; + } + } + + .mp-customer-detail-card__actions { + display: flex; + gap: 8px; + justify-content: flex-end; + margin-top: 12px; + padding-top: 10px; + border-top: 1px dashed #dcebe4; + + .mp-wv-btn { + flex: 0 0 auto; + height: 30px; + min-height: 30px; + padding: 0 12px; + font-size: 12px; + line-height: 1; + border-radius: 8px; + } + } + + .status-tag { + display: inline-flex; + flex-shrink: 0; + align-items: center; + min-height: 24px; + padding: 2px 8px; + font-size: 11px; + font-weight: 600; + line-height: 1.2; + border: 1px solid transparent; + border-radius: 999px; + } + + .status-tag--success, + .status-tag--primary { + color: #087f57; + background: #e9f9f2; + border-color: #b7ebce; + } + + .status-tag--warning { + color: #a16207; + background: #fffbeb; + border-color: #fde68a; + } + + .status-tag--danger { + color: #b91c1c; + background: #fef2f2; + border-color: #fecaca; + } + + .status-tag--info { + color: #475569; + background: #f1f5f9; + border-color: #dbe4ee; + } +} + +.mp-customer-follow-panel { + .mp-customer-follow-att { + margin-top: 10px; + } + + .mp-customer-follow-del { + min-width: 88px; + } +} + +/* 联系人 / 开票 / 关联 Tab:共用检索+三栏筛选节奏 */ +.mp-customer-contact-panel, +.mp-customer-invoice-panel, +.mp-customer-related-panel { + position: relative; + z-index: 1; + overflow: visible; + + .mp-customer-contact-search { + width: 100%; + margin: 0; + } + + /* 检索下紧贴筛选:对齐客户列表三栏结构(灰底页用深绿字) */ + .mp-customer-contact-dd { + position: relative; + z-index: 30; + margin: 4px 0 12px; + background: transparent; + border: 0; + box-shadow: none; + + --van-dropdown-menu-height: 36px; + --van-dropdown-menu-background: transparent; + --van-dropdown-menu-shadow: none; + --van-dropdown-menu-title-font-size: 13px; + --van-dropdown-menu-title-text-color: #6b7b74; + --van-dropdown-menu-title-active-text-color: #087f57; + --van-dropdown-item-z-index: 40; + + .van-dropdown-menu__bar { + position: relative; + z-index: 31; + background: transparent !important; + box-shadow: none !important; + } + + .van-dropdown-menu__title { + color: #6b7b74; + } + + .van-dropdown-menu__title--active { + color: #087f57; + font-weight: 700; + } + } + + /* 卡片压低,避免盖住下拉面板 */ + .mp-customer-detail-card { + position: relative; + z-index: 0; + } + + .mp-customer-contact-actions .mp-wv-btn { + min-width: 72px; + } +} + +/* 详情弹层内资料风分区(商机/合同/日志);表单弹层仍走上方白底 fields */ +.mp-customer-pool-vant__body { + .mp-wv-section { + margin: 0 0 14px; + padding: 0 12px; + background: transparent; + border: 0; + box-shadow: none; + + > h3 { + margin: 0 0 8px; + padding: 0 4px; + font-size: 15px; + font-weight: 700; + color: #1f2a26; + } + + .mp-customer-fields-vant { + margin: 0; + overflow: visible; + background: transparent !important; + border-radius: 0; + + .van-cell { + background: transparent !important; + } + } + } + + .mp-customer-detail-card { + margin-bottom: 10px; + } +} + +/* 联系人筛选 teleport 到 body 后,保证盖住详情列表 */ +body > .van-dropdown-item { + z-index: 3000 !important; +} + +.mp-customer-follow-upload { + margin: 10px 12px 0; + padding: 12px; + background: #fff; + border: 1px solid #dcebe4; + border-radius: 12px; +} + +.mp-customer-filter-vant__foot, +.mp-customer-pool-vant__foot { + display: flex; + gap: 8px; + padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); + background: #fff; + border-top: 1px solid #e8f0ec; + + /* 与列表卡片底栏 .mp-wv-card-actions .mp-wv-btn 一致 */ + .mp-wv-btn, + .van-button { + flex: 1; + height: 30px !important; + min-height: 30px !important; + padding: 0 10px !important; + font-size: 13px !important; + line-height: 1 !important; + border-radius: 8px !important; + } + + .mp-wv-btn--primary, + .van-button--primary { + font-weight: 600; + } +} + +.mp-customer-pool-vant__tip { + margin: 0 16px 12px; + font-size: 13px; + line-height: 1.5; + color: #6b7b74; +} + +.mp-customer-filter-vant__multi { + display: flex; + flex-direction: column; + height: 100%; + background: #f7faf8; +} + +.mp-customer-enum-toolbar { + display: flex; + gap: 10px; + align-items: center; + justify-content: space-between; + padding: 10px 16px 6px; + background: #fff; + border-bottom: 1px solid #e8f0ec; +} + +.mp-customer-enum-toolbar__count { + font-size: 13px; + color: #6b7b74; + + strong { + margin: 0 2px; + color: #0a9f6d; + } +} + +.mp-customer-enum-toolbar__acts { + display: flex; + gap: 8px; + + .mp-wv-btn { + height: 30px; + min-height: 30px; + padding: 0 10px; + font-size: 12px; + line-height: 1; + border-radius: 8px; + } +} + +.mp-customer-enum-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; + max-height: 72px; + padding: 8px 16px 10px; + overflow: auto; + background: #fff; + border-bottom: 1px solid #e8f0ec; +} + +.mp-customer-filter-vant__checks { + flex: 1; + overflow: auto; + padding: 8px 12px 16px; + -webkit-overflow-scrolling: touch; + + .van-cell-group { + overflow: hidden; + border-radius: 12px; + } +} + +.mp-customer-enum-loading { + display: flex; + justify-content: center; + padding: 32px 16px; +} + +.mp-customer-enum-option { + align-items: center; + + &.is-checked { + background: #ecfdf5; + + .mp-customer-enum-option__label { + font-weight: 600; + color: #087f57; + } + } +} + +.mp-customer-enum-option__label { + font-size: 14px; + color: #1f2a26; + line-height: 1.4; +} + +.mp-customer-fields-vant { + margin: 0; +} + +.mp-customer-panel-card { + margin-bottom: 10px; + + > .mp-customer-tags, + > .mp-customer-value, + > .mp-wv-card__meta, + > p, + > .mp-customer-actions, + > .mp-customer-attachments { + padding-right: var(--van-cell-horizontal-padding, 16px); + padding-left: var(--van-cell-horizontal-padding, 16px); + } + + > .mp-customer-tags { + padding-top: 8px; + } + + > .mp-customer-actions:last-child { + padding-bottom: 12px; + } + + > .van-tag { + margin: 8px var(--van-cell-horizontal-padding, 16px) 0; + } +} + +/* teleport 到 body 的底部弹层:保证 82% 高度下内容区可滚动 */ +.mp-customer-vant-popup.van-popup { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.mp-customer-vant-popup { + --van-primary-color: #0a9f6d; + --van-button-primary-background: #0a9f6d; + --van-button-primary-border-color: #0a9f6d; + --van-nav-bar-icon-color: #0a9f6d; + --van-nav-bar-text-color: #0a9f6d; + --van-checkbox-checked-icon-color: #0a9f6d; + --van-picker-confirm-action-color: #0a9f6d; +} + +.van-picker__confirm, +.van-dialog__confirm { + color: #0a9f6d !important; +} + +/* + * 客户管理全站按钮统一为列表卡片 .mp-wv-btn 样式 + * (含 teleport 弹层 / Dialog;FAB 圆形主按钮除外) + */ +@mixin mp-customer-card-btn { + box-sizing: border-box; + height: 30px !important; + min-height: 30px !important; + padding: 0 10px !important; + font-size: 13px !important; + font-weight: 500; + line-height: 1 !important; + border-radius: 8px !important; + + &.van-button--normal, + &.van-button--small, + &.van-button--mini, + &.van-button--large { + height: 30px !important; + min-height: 30px !important; + padding: 0 10px !important; + font-size: 13px !important; + } + + &.van-button--round { + border-radius: 8px !important; + } + + &.van-button--default, + &.van-button--plain { + color: #1f2a26 !important; + background: #fff !important; + border: 1px solid #dcebe4 !important; + } + + &.van-button--plain.van-button--primary { + color: #0a9f6d !important; + background: #fff !important; + border: 1px solid #dcebe4 !important; + } + + &.van-button--primary:not(.van-button--plain) { + color: #fff !important; + background: #0a9f6d !important; + border: 1px solid #0a9f6d !important; + font-weight: 600; + } + + &.van-button--danger:not(.van-button--plain) { + color: #fff !important; + background: #dc2626 !important; + border: 1px solid #dc2626 !important; + font-weight: 600; + } + + &.van-button--danger.van-button--plain { + color: #dc2626 !important; + background: #fff !important; + border: 1px solid #fecaca !important; + } + + &.van-button--block { + width: 100%; + } + + &.van-button--disabled, + &[disabled] { + opacity: 0.45; + } +} + +.mp-customer-page .van-button:not(.mp-wv-speed-main), +.mp-customer-sheet .van-button, +.mp-customer-filter-vant .van-button, +.mp-customer-pool-vant .van-button, +.mp-customer-vant-popup .van-button, +.mp-customer-enum-popup .van-button, +.mp-customer-fields-vant .van-button { + @include mp-customer-card-btn; +} + +/* + * 未走 mp-crm-dialog 的旧 Dialog 仍套卡片按钮; + * 已美化的 .mp-crm-dialog 排除,避免压扁方案 A/B 实心键。 + */ +body .van-dialog:not(.mp-crm-dialog) .van-button { + @include mp-customer-card-btn; + flex: 1; + margin: 0 4px; + height: 30px !important; + min-height: 30px !important; +} + +.mp-customer-filter-vant__foot, +.mp-customer-pool-vant__foot { + .van-button { + flex: 1; + @include mp-customer-card-btn; + height: 30px !important; + min-height: 30px !important; + font-size: 13px !important; + } +} + +.mp-customer-batch-bar .van-button, +.mp-customer-page .mp-wv-fab-bar .van-button, +.mp-customer-page .mp-customer-actions .van-button { + @include mp-customer-card-btn; +} + +.mp-customer-page .mp-wv-speed-menu .van-button { + @include mp-customer-card-btn; + min-width: 96px; +} + +/* FAB 主按钮保持圆形 */ +.mp-customer-page .mp-wv-speed-main.van-button { + width: 52px !important; + height: 52px !important; + min-width: 52px !important; + min-height: 52px !important; + padding: 0 !important; + border-radius: 50% !important; +} diff --git a/web-admin/src/views/m/crm/customer/detail.vue b/web-admin/src/views/m/crm/customer/detail.vue new file mode 100644 index 000000000..cda18a7e4 --- /dev/null +++ b/web-admin/src/views/m/crm/customer/detail.vue @@ -0,0 +1,234 @@ + + diff --git a/web-admin/src/views/m/crm/customer/form.vue b/web-admin/src/views/m/crm/customer/form.vue new file mode 100644 index 000000000..75988b609 --- /dev/null +++ b/web-admin/src/views/m/crm/customer/form.vue @@ -0,0 +1,210 @@ + + + diff --git a/web-admin/src/views/m/crm/customer/index.vue b/web-admin/src/views/m/crm/customer/index.vue new file mode 100644 index 000000000..e1f10fac1 --- /dev/null +++ b/web-admin/src/views/m/crm/customer/index.vue @@ -0,0 +1,535 @@ + + + diff --git a/web-admin/src/views/m/crm/product/product.scss b/web-admin/src/views/m/crm/product/product.scss index ff4a56bd5..5598b9b53 100644 --- a/web-admin/src/views/m/crm/product/product.scss +++ b/web-admin/src/views/m/crm/product/product.scss @@ -291,119 +291,4 @@ body > .van-dropdown-item { z-index: 3000 !important; } -/* ── 确认弹窗:方案 A / B(挂到 body,须用 className 限定;对齐 Demo)── */ -body .van-dialog.mp-product-dialog { - overflow: hidden; - width: 300px !important; - max-width: calc(100vw - 48px); - border-radius: 16px; - box-shadow: 0 16px 40px rgb(15 40 30 / 22%); -} - -/* 方案 A:Vant header 居中 */ -body .van-dialog.mp-product-dialog .van-dialog__header { - padding: 20px 20px 0; - font-size: 16px; - font-weight: 700; - color: #1f2a26; - line-height: 1.4; - text-align: center; -} - -body .van-dialog.mp-product-dialog .van-dialog__content { - padding: 0; -} - -body .van-dialog.mp-product-dialog .van-dialog__message { - padding: 10px 20px 20px; - font-size: 14px; - line-height: 1.55; - color: #6b7b74; - text-align: center; -} - -/* 方案 B:图标 → 标题 → 文案(标题在 HTML 内,无 Vant header) */ -body .van-dialog.mp-product-dialog.mp-product-dialog--danger .van-dialog__header { - display: none; -} - -body .van-dialog.mp-product-dialog.mp-product-dialog--danger .van-dialog__message { - padding: 20px 20px 20px; -} - -body .van-dialog.mp-product-dialog .mp-product-dialog__warn { - display: flex; - justify-content: center; - margin: 0 0 12px; -} - -body .van-dialog.mp-product-dialog .mp-product-dialog__warn-icon { - display: flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - font-size: 22px; - font-weight: 700; - line-height: 1; - color: #dc2626; - background: #fef2f2; - border-radius: 50%; -} - -body .van-dialog.mp-product-dialog .mp-product-dialog__title { - margin: 0 0 8px; - font-size: 16px; - font-weight: 700; - line-height: 1.4; - color: #1f2a26; - text-align: center; -} - -body .van-dialog.mp-product-dialog .mp-product-dialog__msg { - color: #6b7b74; - text-align: center; -} - -body .van-dialog.mp-product-dialog .van-dialog__footer { - display: flex; - gap: 10px; - padding: 0 16px 16px; - border-top: none !important; -} - -body .van-dialog.mp-product-dialog .van-dialog__footer::after, -body .van-dialog.mp-product-dialog .van-hairline--left::after, -body .van-dialog.mp-product-dialog .van-hairline--top::after { - display: none !important; -} - -body .van-dialog.mp-product-dialog .van-dialog__cancel, -body .van-dialog.mp-product-dialog .van-dialog__confirm { - flex: 1; - height: 40px !important; - min-height: 40px !important; - margin: 0 !important; - padding: 0 10px !important; - font-size: 14px !important; - font-weight: 600 !important; - line-height: 40px !important; - border-radius: 10px !important; - border: 1px solid #dcebe4 !important; -} - -body .van-dialog.mp-product-dialog .van-dialog__cancel { - color: #1f2a26 !important; - background: #fff !important; -} - -body .van-dialog.mp-product-dialog .van-dialog__confirm { - color: #fff !important; - background: #0a9f6d !important; - border-color: #0a9f6d !important; -} - -body .van-dialog.mp-product-dialog.mp-product-dialog--danger .van-dialog__confirm { - background: #dc2626 !important; - border-color: #dc2626 !important; -} +/* 确认弹窗样式已上收至 @/styles/mp-crm-primitives.scss(.mp-crm-dialog) */