feat(order-service): 增加客户页自定义公告
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
-- 133_order_service_profile_announcement.sql
|
||||
-- 订单服务客户页公告:草稿保存公告内容与展示样式,发布时随配置写入版本快照。
|
||||
|
||||
ALTER TABLE order_service_profiles
|
||||
ADD COLUMN IF NOT EXISTS announcement_json JSONB NOT NULL DEFAULT '{}'::jsonb;
|
||||
|
||||
COMMENT ON COLUMN order_service_profiles.announcement_json IS
|
||||
'客户页公告:enabled/title/content/fontSize/color/backgroundColor/fontWeight/textAlign';
|
||||
@@ -12,6 +12,7 @@ export type OrderServiceProfileRow = {
|
||||
fields_json: Array<Record<string, unknown>>
|
||||
screenshots_json: Record<string, unknown>
|
||||
auto_publish_json: Record<string, unknown>
|
||||
announcement_json: Record<string, unknown>
|
||||
submit_text: string
|
||||
success_text: string
|
||||
status: string
|
||||
@@ -98,6 +99,7 @@ export async function upsertOrderServiceProfile(input: {
|
||||
fields: Array<Record<string, unknown>>
|
||||
screenshots: Record<string, unknown>
|
||||
autoPublish: Record<string, unknown>
|
||||
announcement: Record<string, unknown>
|
||||
submitText: string
|
||||
successText: string
|
||||
adminUserId: number | null
|
||||
@@ -106,12 +108,12 @@ export async function upsertOrderServiceProfile(input: {
|
||||
`
|
||||
INSERT INTO order_service_profiles (
|
||||
category_id, name, title, description,
|
||||
guide_image_json, fields_json, screenshots_json, auto_publish_json,
|
||||
guide_image_json, fields_json, screenshots_json, auto_publish_json, announcement_json,
|
||||
submit_text, success_text, status, created_by_admin_user_id
|
||||
) VALUES (
|
||||
$1, $2, $3, $4,
|
||||
$5::jsonb, $6::jsonb, $7::jsonb, $8::jsonb,
|
||||
$9, $10, 'draft', $11
|
||||
$5::jsonb, $6::jsonb, $7::jsonb, $8::jsonb, $9::jsonb,
|
||||
$10, $11, 'draft', $12
|
||||
)
|
||||
ON CONFLICT (category_id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
@@ -121,6 +123,7 @@ export async function upsertOrderServiceProfile(input: {
|
||||
fields_json = EXCLUDED.fields_json,
|
||||
screenshots_json = EXCLUDED.screenshots_json,
|
||||
auto_publish_json = EXCLUDED.auto_publish_json,
|
||||
announcement_json = EXCLUDED.announcement_json,
|
||||
submit_text = EXCLUDED.submit_text,
|
||||
success_text = EXCLUDED.success_text,
|
||||
current_version_id = CASE
|
||||
@@ -140,6 +143,7 @@ export async function upsertOrderServiceProfile(input: {
|
||||
JSON.stringify(input.fields),
|
||||
JSON.stringify(input.screenshots),
|
||||
JSON.stringify(input.autoPublish),
|
||||
JSON.stringify(input.announcement),
|
||||
input.submitText,
|
||||
input.successText,
|
||||
input.adminUserId,
|
||||
|
||||
@@ -77,3 +77,42 @@ test('订单服务配置规范化:未知字段类型降级为文本,空下
|
||||
assert.equal(normalized.fields.length, 1)
|
||||
assert.equal(normalized.fields[0]?.type, 'text')
|
||||
})
|
||||
|
||||
test('订单服务页面公告:旧配置默认展示资料填写错误免责声明', () => {
|
||||
const normalized = config()
|
||||
assert.deepEqual(normalized.announcement, {
|
||||
enabled: true,
|
||||
title: '公告',
|
||||
content: '由于个人资料填写错误,导致的赠送错误,店铺概不负责!',
|
||||
fontSize: 14,
|
||||
color: '#8a4b00',
|
||||
backgroundColor: '#fff7e6',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
})
|
||||
})
|
||||
|
||||
test('订单服务页面公告:支持自定义样式并拦截非法颜色与越界字号', () => {
|
||||
const normalized = config({
|
||||
announcement: {
|
||||
enabled: false,
|
||||
title: '填写前必看',
|
||||
content: '请确认资料无误后再提交。',
|
||||
fontSize: 99,
|
||||
color: 'red;position:fixed',
|
||||
backgroundColor: '#123456',
|
||||
fontWeight: 'normal',
|
||||
textAlign: 'center',
|
||||
},
|
||||
})
|
||||
assert.deepEqual(normalized.announcement, {
|
||||
enabled: false,
|
||||
title: '填写前必看',
|
||||
content: '请确认资料无误后再提交。',
|
||||
fontSize: 24,
|
||||
color: '#8a4b00',
|
||||
backgroundColor: '#123456',
|
||||
fontWeight: 'normal',
|
||||
textAlign: 'center',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -47,6 +47,17 @@ export type OrderServiceAutoPublish = {
|
||||
retryMax: number
|
||||
}
|
||||
|
||||
export type OrderServiceAnnouncement = {
|
||||
enabled: boolean
|
||||
title: string
|
||||
content: string
|
||||
fontSize: number
|
||||
color: string
|
||||
backgroundColor: string
|
||||
fontWeight: 'normal' | 'bold'
|
||||
textAlign: 'left' | 'center'
|
||||
}
|
||||
|
||||
export type OrderServiceProfileConfig = {
|
||||
name: string
|
||||
title: string
|
||||
@@ -55,6 +66,7 @@ export type OrderServiceProfileConfig = {
|
||||
fields: OrderServiceField[]
|
||||
screenshots: OrderServiceScreenshots
|
||||
autoPublish: OrderServiceAutoPublish
|
||||
announcement: OrderServiceAnnouncement
|
||||
submitText: string
|
||||
successText: string
|
||||
}
|
||||
@@ -76,6 +88,17 @@ const FIELD_TYPES = new Set<OrderServiceFieldType>([
|
||||
'date',
|
||||
])
|
||||
|
||||
export const DEFAULT_ORDER_SERVICE_ANNOUNCEMENT: OrderServiceAnnouncement = {
|
||||
enabled: true,
|
||||
title: '公告',
|
||||
content: '由于个人资料填写错误,导致的赠送错误,店铺概不负责!',
|
||||
fontSize: 14,
|
||||
color: '#8a4b00',
|
||||
backgroundColor: '#fff7e6',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
}
|
||||
|
||||
export async function listAdminOrderServiceProfiles() {
|
||||
const [categories, profiles] = await Promise.all([
|
||||
listAllWorkCategories(),
|
||||
@@ -115,6 +138,7 @@ export async function saveAdminOrderServiceProfile(payload: JsonObject = {}) {
|
||||
const fields = normalizeFields(payload.fields ?? payload.fields_json)
|
||||
const screenshots = normalizeScreenshots(payload.screenshots ?? payload.screenshots_json)
|
||||
const autoPublish = normalizeAutoPublish(payload.autoPublish ?? payload.auto_publish_json)
|
||||
const announcement = normalizeAnnouncement(payload.announcement ?? payload.announcement_json)
|
||||
const guideImage = normalizeGuideImage(payload.guideImage ?? payload.guide_image_json)
|
||||
const adminUserId = Number(payload.adminUserId ?? payload.admin_user_id ?? 0) || null
|
||||
|
||||
@@ -127,6 +151,7 @@ export async function saveAdminOrderServiceProfile(payload: JsonObject = {}) {
|
||||
fields,
|
||||
screenshots,
|
||||
autoPublish,
|
||||
announcement,
|
||||
submitText: text(payload.submitText, 30) || '提交资料',
|
||||
successText: text(payload.successText, 80) || '资料已提交,请等待处理',
|
||||
adminUserId,
|
||||
@@ -204,6 +229,7 @@ function mapAdminProfile(row: OrderServiceProfileRow) {
|
||||
fields: row.fields_json,
|
||||
screenshots: row.screenshots_json,
|
||||
autoPublish: row.auto_publish_json,
|
||||
announcement: normalizeAnnouncement(row.announcement_json),
|
||||
submitText: row.submit_text,
|
||||
successText: row.success_text,
|
||||
status: row.status,
|
||||
@@ -222,6 +248,7 @@ export function buildProfileConfig(row: {
|
||||
fields_json: unknown
|
||||
screenshots_json: unknown
|
||||
auto_publish_json: unknown
|
||||
announcement_json: unknown
|
||||
submit_text: string
|
||||
success_text: string
|
||||
}): OrderServiceProfileConfig & { guideImage: Record<string, unknown> } {
|
||||
@@ -233,6 +260,7 @@ export function buildProfileConfig(row: {
|
||||
fields: normalizeFields(row.fields_json),
|
||||
screenshots: normalizeScreenshots(row.screenshots_json),
|
||||
autoPublish: normalizeAutoPublish(row.auto_publish_json),
|
||||
announcement: normalizeAnnouncement(row.announcement_json),
|
||||
submitText: row.submit_text || '提交资料',
|
||||
successText: row.success_text || '资料已提交,请等待处理',
|
||||
}
|
||||
@@ -284,6 +312,7 @@ export function normalizeProfileConfig(value: JsonObject): OrderServiceProfileCo
|
||||
fields: normalizeFields(value.fields),
|
||||
screenshots: normalizeScreenshots(value.screenshots),
|
||||
autoPublish: normalizeAutoPublish(value.autoPublish),
|
||||
announcement: normalizeAnnouncement(value.announcement),
|
||||
submitText: text(value.submitText, 30) || '提交资料',
|
||||
successText: text(value.successText, 80) || '资料已提交,请等待处理',
|
||||
}
|
||||
@@ -362,11 +391,38 @@ function normalizeAutoPublish(value: unknown): OrderServiceAutoPublish {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAnnouncement(value: unknown): OrderServiceAnnouncement {
|
||||
const source = value && typeof value === 'object' ? (value as JsonObject) : {}
|
||||
return {
|
||||
enabled: source.enabled !== false,
|
||||
title: text(source.title, 30) || DEFAULT_ORDER_SERVICE_ANNOUNCEMENT.title,
|
||||
content: text(source.content, 500) || DEFAULT_ORDER_SERVICE_ANNOUNCEMENT.content,
|
||||
fontSize: clampInteger(source.fontSize ?? source.font_size, 14, 12, 24),
|
||||
color: normalizeHexColor(source.color, DEFAULT_ORDER_SERVICE_ANNOUNCEMENT.color),
|
||||
backgroundColor: normalizeHexColor(
|
||||
source.backgroundColor ?? source.background_color,
|
||||
DEFAULT_ORDER_SERVICE_ANNOUNCEMENT.backgroundColor,
|
||||
),
|
||||
fontWeight:
|
||||
source.fontWeight === 'normal' || source.font_weight === 'normal'
|
||||
? 'normal'
|
||||
: source.fontWeight === 'bold' || source.font_weight === 'bold'
|
||||
? 'bold'
|
||||
: DEFAULT_ORDER_SERVICE_ANNOUNCEMENT.fontWeight,
|
||||
textAlign: source.textAlign === 'center' || source.text_align === 'center' ? 'center' : 'left',
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeGuideImage(value: unknown): Record<string, unknown> {
|
||||
const normalized = normalizeUploadedFiles([value])[0]
|
||||
return normalized ? { ...normalized } : {}
|
||||
}
|
||||
|
||||
function normalizeHexColor(value: unknown, fallback: string) {
|
||||
const normalized = text(value, 7)
|
||||
return /^#[0-9a-fA-F]{6}$/.test(normalized) ? normalized.toLowerCase() : fallback
|
||||
}
|
||||
|
||||
function clampInteger(value: unknown, fallback: number, min: number, max: number) {
|
||||
const parsed = Number(value)
|
||||
if (!Number.isFinite(parsed)) return fallback
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
throwPublicMaterialBlocked,
|
||||
} from './work-order-material-link-service.js'
|
||||
import {
|
||||
DEFAULT_ORDER_SERVICE_ANNOUNCEMENT,
|
||||
getOrderServiceProfileSnapshotByVersion,
|
||||
normalizeProfileConfig,
|
||||
type OrderServiceField,
|
||||
@@ -77,6 +78,7 @@ export type OrderServicePublicItem = {
|
||||
message: string
|
||||
canSubmit: boolean
|
||||
blockedReason: string
|
||||
announcement?: OrderServiceProfileConfig['announcement']
|
||||
fields?: OrderServiceField[]
|
||||
values?: Record<string, string>
|
||||
screenshots?: Array<Record<string, unknown>>
|
||||
@@ -369,6 +371,7 @@ async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePubl
|
||||
}),
|
||||
canSubmit: state.canSubmit,
|
||||
blockedReason: state.blockedReason,
|
||||
...(profile?.announcement ? { announcement: profile.announcement } : {}),
|
||||
}
|
||||
|
||||
if (!state.canSubmit) return base
|
||||
@@ -426,6 +429,7 @@ async function resolveProfileConfig(
|
||||
helpText: '',
|
||||
},
|
||||
autoPublish: { enabled: false, requireVoucherConsume: true, retryMax: 3 },
|
||||
announcement: DEFAULT_ORDER_SERVICE_ANNOUNCEMENT,
|
||||
submitText: '提交资料',
|
||||
successText: '资料已提交,请等待处理',
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
saveAdminOrderServiceProfile,
|
||||
type AdminOrderServiceAutoPublishJob,
|
||||
type AdminOrderServiceProfile,
|
||||
type OrderServiceAnnouncement,
|
||||
type OrderServiceField,
|
||||
} from '@/services/admin/worker-platform'
|
||||
import { uploadFile } from '@/services/files'
|
||||
@@ -71,6 +72,7 @@ type OrderServiceFormValues = {
|
||||
successText: string
|
||||
screenshots: ScreenshotForm
|
||||
autoPublish: AutoPublishForm
|
||||
announcement: OrderServiceAnnouncement
|
||||
}
|
||||
|
||||
const DEFAULT_SCREENSHOTS: ScreenshotForm = {
|
||||
@@ -88,6 +90,17 @@ const DEFAULT_AUTO_PUBLISH: AutoPublishForm = {
|
||||
retryMax: 3,
|
||||
}
|
||||
|
||||
const DEFAULT_ANNOUNCEMENT: OrderServiceAnnouncement = {
|
||||
enabled: true,
|
||||
title: '公告',
|
||||
content: '由于个人资料填写错误,导致的赠送错误,店铺概不负责!',
|
||||
fontSize: 14,
|
||||
color: '#8a4b00',
|
||||
backgroundColor: '#fff7e6',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
}
|
||||
|
||||
export default function OrderServicePanel() {
|
||||
const { message } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -141,6 +154,7 @@ export default function OrderServicePanel() {
|
||||
(file: File, scene: string) => uploadFile(file, scene, 'admin'),
|
||||
[],
|
||||
)
|
||||
const announcementPreview = Form.useWatch('announcement', form) || DEFAULT_ANNOUNCEMENT
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCategoryId !== null || categories.length === 0) return
|
||||
@@ -160,6 +174,7 @@ export default function OrderServicePanel() {
|
||||
successText: nextProfile?.successText || '资料已提交,请等待处理',
|
||||
screenshots: nextProfile?.screenshots || DEFAULT_SCREENSHOTS,
|
||||
autoPublish: nextProfile?.autoPublish || DEFAULT_AUTO_PUBLISH,
|
||||
announcement: nextProfile?.announcement || DEFAULT_ANNOUNCEMENT,
|
||||
})
|
||||
setFields(normalizeFieldOrder(nextProfile?.fields || []))
|
||||
setGuideImage(nextProfile?.guideImage || null)
|
||||
@@ -232,6 +247,7 @@ export default function OrderServicePanel() {
|
||||
fields: normalizedFields,
|
||||
screenshots: values.screenshots,
|
||||
autoPublish: values.autoPublish,
|
||||
announcement: values.announcement,
|
||||
submitText: values.submitText,
|
||||
successText: values.successText,
|
||||
})
|
||||
@@ -509,6 +525,83 @@ export default function OrderServicePanel() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="order-service-section">
|
||||
<div className="order-service-section-head">
|
||||
<div>
|
||||
<Text strong>页面公告</Text>
|
||||
<Text type="secondary">显示在客户资料页顶部,可自定义内容和文字样式。</Text>
|
||||
</div>
|
||||
<Space size={8}>
|
||||
<Text type="secondary">显示公告</Text>
|
||||
<Form.Item
|
||||
name={['announcement', 'enabled']}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</div>
|
||||
<Row gutter={[12, 0]}>
|
||||
<Col xs={24} md={10}>
|
||||
<Form.Item label="公告标题" name={['announcement', 'title']}>
|
||||
<Input maxLength={30} placeholder="例如:公告" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} md={4}>
|
||||
<Form.Item label="字号" name={['announcement', 'fontSize']}>
|
||||
<InputNumber
|
||||
min={12}
|
||||
max={24}
|
||||
addonAfter="px"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} md={5}>
|
||||
<Form.Item label="文字颜色" name={['announcement', 'color']}>
|
||||
<Input type="color" className="order-service-color-input" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} md={5}>
|
||||
<Form.Item label="背景颜色" name={['announcement', 'backgroundColor']}>
|
||||
<Input type="color" className="order-service-color-input" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} md={4}>
|
||||
<Form.Item label="字重" name={['announcement', 'fontWeight']}>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'normal', label: '正常' },
|
||||
{ value: 'bold', label: '加粗' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} md={4}>
|
||||
<Form.Item label="对齐" name={['announcement', 'textAlign']}>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'left', label: '左对齐' },
|
||||
{ value: 'center', label: '居中' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="公告内容" name={['announcement', 'content']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 2, maxRows: 5 }}
|
||||
maxLength={500}
|
||||
showCount
|
||||
placeholder="请输入需要客户注意的公告内容"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<AnnouncementPreview announcement={announcementPreview} />
|
||||
</section>
|
||||
|
||||
<section className="order-service-section">
|
||||
<div className="order-service-section-head">
|
||||
<div>
|
||||
@@ -809,3 +902,29 @@ function resolveProfileStatus(profile: AdminOrderServiceProfile | undefined) {
|
||||
}
|
||||
return { label: '已归档', tone: 'muted' }
|
||||
}
|
||||
|
||||
function AnnouncementPreview({ announcement }: { announcement: OrderServiceAnnouncement }) {
|
||||
return (
|
||||
<div
|
||||
className={`order-service-announcement-preview${announcement.enabled ? '' : ' is-disabled'}`}
|
||||
style={{
|
||||
color: announcement.color,
|
||||
backgroundColor: announcement.backgroundColor,
|
||||
textAlign: announcement.textAlign,
|
||||
}}
|
||||
>
|
||||
<Text strong className="order-service-announcement-preview-title">
|
||||
{announcement.title || '公告'}
|
||||
</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: announcement.fontSize,
|
||||
fontWeight: announcement.fontWeight,
|
||||
}}
|
||||
>
|
||||
{announcement.content || '公告内容'}
|
||||
</div>
|
||||
{!announcement.enabled ? <span>当前已关闭,不会在客户页显示</span> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -261,6 +261,31 @@ export default function OrderServicePage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{pageConfig?.announcement?.enabled && pageConfig.announcement.content ? (
|
||||
<section
|
||||
aria-label={pageConfig.announcement.title || '页面公告'}
|
||||
className="collect-link-announcement"
|
||||
style={{
|
||||
color: pageConfig.announcement.color,
|
||||
backgroundColor: pageConfig.announcement.backgroundColor,
|
||||
textAlign: pageConfig.announcement.textAlign,
|
||||
}}
|
||||
>
|
||||
<strong className="collect-link-announcement-title">
|
||||
{pageConfig.announcement.title || '公告'}
|
||||
</strong>
|
||||
<div
|
||||
className="collect-link-announcement-content"
|
||||
style={{
|
||||
fontSize: pageConfig.announcement.fontSize,
|
||||
fontWeight: pageConfig.announcement.fontWeight,
|
||||
}}
|
||||
>
|
||||
{pageConfig.announcement.content}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{submittedNames.length > 0 ? (
|
||||
<Alert
|
||||
type="success"
|
||||
|
||||
@@ -1120,6 +1120,17 @@ export type OrderServiceField = {
|
||||
sortOrder?: number
|
||||
}
|
||||
|
||||
export type OrderServiceAnnouncement = {
|
||||
enabled: boolean
|
||||
title: string
|
||||
content: string
|
||||
fontSize: number
|
||||
color: string
|
||||
backgroundColor: string
|
||||
fontWeight: 'normal' | 'bold'
|
||||
textAlign: 'left' | 'center'
|
||||
}
|
||||
|
||||
export type AdminOrderServiceProfile = {
|
||||
id: number
|
||||
categoryId: number
|
||||
@@ -1141,6 +1152,7 @@ export type AdminOrderServiceProfile = {
|
||||
requireVoucherConsume: boolean
|
||||
retryMax: number
|
||||
}
|
||||
announcement: OrderServiceAnnouncement
|
||||
submitText: string
|
||||
successText: string
|
||||
status: 'draft' | 'published' | 'archived' | string
|
||||
@@ -1187,6 +1199,7 @@ export function saveAdminOrderServiceProfile(payload: {
|
||||
requireVoucherConsume: boolean
|
||||
retryMax: number
|
||||
}
|
||||
announcement: OrderServiceAnnouncement
|
||||
submitText?: string
|
||||
successText?: string
|
||||
}) {
|
||||
|
||||
@@ -576,6 +576,17 @@ export type OrderServiceScreenshots = {
|
||||
helpText?: string
|
||||
}
|
||||
|
||||
export type OrderServiceAnnouncement = {
|
||||
enabled: boolean
|
||||
title: string
|
||||
content: string
|
||||
fontSize: number
|
||||
color: string
|
||||
backgroundColor: string
|
||||
fontWeight: 'normal' | 'bold'
|
||||
textAlign: 'left' | 'center'
|
||||
}
|
||||
|
||||
export type OrderServiceItem = {
|
||||
workOrderId: number
|
||||
productName: string
|
||||
@@ -589,6 +600,7 @@ export type OrderServiceItem = {
|
||||
message: string
|
||||
canSubmit: boolean
|
||||
blockedReason: '' | 'already_submitted' | 'status' | 'assigned' | 'shares'
|
||||
announcement?: OrderServiceAnnouncement
|
||||
fields?: OrderServiceField[]
|
||||
values?: Record<string, string>
|
||||
screenshots?: UploadedFile[]
|
||||
|
||||
@@ -1843,6 +1843,34 @@
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.order-service-color-input {
|
||||
height: 32px;
|
||||
padding: 2px 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.order-service-announcement-preview {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid rgba(138, 75, 0, 0.16);
|
||||
border-left-width: 4px;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
.order-service-announcement-preview-title {
|
||||
color: inherit !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.order-service-announcement-preview.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.order-service-announcement-preview > span:last-child {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.order-service-field-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
|
||||
@@ -515,6 +515,25 @@
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.collect-link-announcement {
|
||||
padding: 12px 14px;
|
||||
border: 1px solid rgba(138, 75, 0, 0.16);
|
||||
border-left-width: 4px;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
.collect-link-announcement-title {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.collect-link-announcement-content {
|
||||
line-height: 1.65;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/* 商品名与订单号放在同一个信息头里,避免标题和灰色信息块分离 */
|
||||
.collect-link-order-head {
|
||||
display: flex;
|
||||
|
||||
@@ -7,7 +7,7 @@ status: stable
|
||||
|
||||
# 订单服务收集与状态页设计
|
||||
|
||||
> 状态:现行。最后校准:2026-11-03。代码分支:`dev_11.3`。
|
||||
> 状态:现行。最后校准:2026-11-04。代码分支:`dev_11.3`。
|
||||
|
||||
## 1. 目标
|
||||
|
||||
@@ -92,6 +92,7 @@ work_orders.service_profile_snapshot_json
|
||||
- 按平台订单号查询一单多工单。
|
||||
- 动态文本、长文本、下拉、单选、数字、日期字段。
|
||||
- 一张分类引导图。
|
||||
- 页面公告,可配置标题、正文、字号、文字颜色、背景颜色、字重和对齐方式;旧配置默认显示资料填写错误免责声明。
|
||||
- 图片截图规则,必填时最少 1 张。
|
||||
- 提交后禁止再次修改。
|
||||
- 每 15 秒、页面重新可见或窗口重新聚焦时刷新状态。
|
||||
|
||||
Reference in New Issue
Block a user