564 lines
20 KiB
Vue
564 lines
20 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<div v-if="showBasic" class="create-section">
|
||
<step-basic-info ref="basicRef" v-model="form.basic" />
|
||
</div>
|
||
|
||
<div class="create-section">
|
||
<step-details
|
||
ref="detailsRef"
|
||
:details="form.details"
|
||
:products="form.products"
|
||
:price-set-id="form.priceSetId"
|
||
:psv-id="form.version.psvId"
|
||
@update:psv-id="onPsvIdUpdate"
|
||
:set-type="form.basic.setType"
|
||
:price-build-mode="form.version.priceBuildMode"
|
||
@update:price-build-mode="onPriceBuildModeUpdate"
|
||
@update:pef-id="onPefIdUpdate"
|
||
:version-no="form.version.versionNo"
|
||
:version-status="form.version.status"
|
||
:ref-data="refData"
|
||
:live-persist="livePersist"
|
||
:build-mode-dirty="buildModeDirty"
|
||
:build-mode-ready="pageReady"
|
||
:show-import-inherit="showImportInherit"
|
||
:header-title="detailsHeaderTitle"
|
||
/>
|
||
</div>
|
||
|
||
<div class="wizard-actions">
|
||
<el-button @click="goBack">取消</el-button>
|
||
<el-button type="primary" :loading="creating" @click="finishEdit">完成</el-button>
|
||
</div>
|
||
|
||
<el-dialog
|
||
v-model="progressVisible"
|
||
:title="mod.labels.saveProgressTitle"
|
||
width="420px"
|
||
:close-on-click-modal="false"
|
||
:close-on-press-escape="false"
|
||
:show-close="false"
|
||
append-to-body
|
||
>
|
||
<div class="save-progress">
|
||
<el-progress :percentage="progressPercent" :status="progressStatus" />
|
||
<p class="save-progress__msg">{{ progressMessage }}</p>
|
||
<p class="save-progress__hint">请勿关闭页面,大量明细保存可能需要一些时间</p>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="PriceSetCreate">
|
||
import StepBasicInfo from './components/create/StepBasicInfo.vue'
|
||
import StepDetails from './components/create/StepDetails.vue'
|
||
import { usePriceSetRefData } from './composables/usePriceSetRefData'
|
||
import {
|
||
createEmptyBasicInfo,
|
||
isBuildModeLockedVersion,
|
||
nextVersionNo,
|
||
toCustomerPayload,
|
||
toDetailPayload
|
||
} from './utils/priceSet'
|
||
import { usePriceSetModule } from './composables/usePriceSetModule'
|
||
|
||
const mod = usePriceSetModule()
|
||
|
||
const { proxy } = getCurrentInstance()
|
||
const route = useRoute()
|
||
|
||
const refData = usePriceSetRefData()
|
||
|
||
const creating = ref(false)
|
||
const progressVisible = ref(false)
|
||
const progressPercent = ref(0)
|
||
const progressMessage = ref('')
|
||
const progressStatus = ref(undefined)
|
||
const basicRef = ref()
|
||
const detailsRef = ref()
|
||
const copyMode = computed(() => String(route.query.copyVersion || '') === '1')
|
||
const newVersionMode = computed(() => String(route.query.newVersion || '') === '1')
|
||
/** copy / newVersion: defer DB write until finish */
|
||
const deferPersist = computed(() => copyMode.value || newVersionMode.value)
|
||
/** Continue-config / copy / new-version: hide basic (package already exists) */
|
||
const showBasic = computed(() => !deferPersist.value && String(route.query.step || '') !== '2')
|
||
/** Edit existing version: live PUT/pull/import. New create / copy / newVersion: defer until finish. */
|
||
const livePersist = computed(() => !!form.priceSetId && !deferPersist.value)
|
||
/** Brand-new package has no prior version — hide IMPORT inherit card. */
|
||
const showImportInherit = computed(() => newVersionMode.value || copyMode.value)
|
||
|
||
const detailsHeaderTitle = '编辑产品价格'
|
||
|
||
const form = reactive({
|
||
priceSetId: undefined,
|
||
basic: createEmptyBasicInfo(),
|
||
version: { psvId: undefined, versionNo: 'V1', effectiveTime: undefined, expireTime: undefined, status: undefined, priceBuildMode: undefined },
|
||
details: [],
|
||
products: [],
|
||
customers: []
|
||
})
|
||
/** Draft IMPORT Excel attachment id; stamped onto prc_price_set_product on finish */
|
||
const draftPefId = ref(undefined)
|
||
const draftPefIds = ref([])
|
||
const savedPriceBuildMode = ref(undefined)
|
||
const savedProductsSnapshot = ref([])
|
||
const buildModeDirty = computed(() =>
|
||
String(form.version.priceBuildMode || '') !== String(savedPriceBuildMode.value || '')
|
||
)
|
||
/** Gate build-mode cards until package payload applied (avoids first click being wiped). */
|
||
const pageReady = ref(!route.query.priceSetId)
|
||
|
||
function mapSaveProgressMessage(message) {
|
||
const m = message || ''
|
||
if (!m) return ''
|
||
if (m === 'queued') return '排队中'
|
||
if (m === 'pulling library') return '拉取产品库'
|
||
if (m === 'details exist, skip pull') return '明细已存在,跳过拉取'
|
||
if (m === 'saving basic') return '保存基本信息'
|
||
if (m === 'saving version') return '保存版本'
|
||
if (m.startsWith('saving details ')) return '保存明细 ' + m.slice('saving details '.length)
|
||
if (m === 'saving surcharges') return '保存附加费'
|
||
if (m === 'details unchanged') return '明细未变更'
|
||
if (m === 'saving customers') return '保存客户'
|
||
if (m === 'finishing') return '收尾中'
|
||
if (m === 'done') return '完成'
|
||
if (m === 'failed') return '失败'
|
||
return m
|
||
}
|
||
|
||
async function runSaveWithProgress(payload) {
|
||
progressVisible.value = true
|
||
progressPercent.value = 0
|
||
progressMessage.value = ''
|
||
progressStatus.value = undefined
|
||
try {
|
||
const result = await mod.api.savePriceSetAsync(payload, (task) => {
|
||
progressPercent.value = Math.min(100, Number(task.percent) || 0)
|
||
progressMessage.value = mapSaveProgressMessage(task.message)
|
||
if (task.status === 'FAIL') {
|
||
progressStatus.value = 'exception'
|
||
} else if (task.status === 'SUCCESS') {
|
||
progressStatus.value = 'success'
|
||
} else {
|
||
progressStatus.value = undefined
|
||
}
|
||
})
|
||
progressPercent.value = 100
|
||
progressStatus.value = 'success'
|
||
return result.priceSetId
|
||
} catch (e) {
|
||
progressStatus.value = 'exception'
|
||
const msg = (e && e.task && e.task.error) || (e && e.message) || '保存失败'
|
||
progressMessage.value = msg
|
||
if (proxy && proxy.$modal) {
|
||
proxy.$modal.msgError(msg)
|
||
}
|
||
await new Promise(resolve => setTimeout(resolve, 800))
|
||
throw e
|
||
} finally {
|
||
progressVisible.value = false
|
||
}
|
||
}
|
||
|
||
/** Finish: create/copy async save all; edit already live-saved. */
|
||
async function finishEdit() {
|
||
if (livePersist.value) {
|
||
if (!form.priceSetId) {
|
||
goBack()
|
||
return
|
||
}
|
||
if (buildModeDirty.value) {
|
||
if (!form.version.priceBuildMode) {
|
||
proxy.$modal.msgWarning("请先选择创建价格方式")
|
||
return
|
||
}
|
||
creating.value = true
|
||
try {
|
||
const payload = buildPayload()
|
||
delete payload.pullFromLibrary
|
||
delete payload.forcePullLibrary
|
||
delete payload.details
|
||
await runSaveWithProgress(payload)
|
||
savedPriceBuildMode.value = form.version.priceBuildMode
|
||
await nextTick()
|
||
if (form.version.priceBuildMode === 'PULL' && detailsRef.value && detailsRef.value.pullAllFromLibrary) {
|
||
await detailsRef.value.pullAllFromLibrary({ silent: true })
|
||
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
|
||
proxy.$modal.msgSuccess("建价方式已保存")
|
||
} else if (form.version.priceBuildMode === 'IMPORT') {
|
||
// Backend copied prior version into this draft; refresh product list from current psv
|
||
await refreshProductsFromCurrentVersion()
|
||
proxy.$modal.msgSuccess("建价方式已保存,已带出上一版产品")
|
||
} else if (form.version.priceBuildMode === 'IMPORT_FRESH') {
|
||
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
|
||
proxy.$modal.msgSuccess("建价方式已保存,可直接导入 Excel 建价(不延用以前的产品价格)")
|
||
} else {
|
||
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
|
||
proxy.$modal.msgSuccess("建价方式已保存")
|
||
}
|
||
} catch (e) {
|
||
return
|
||
} finally {
|
||
creating.value = false
|
||
}
|
||
} else {
|
||
proxy.$modal.msgSuccess("修改已实时落库")
|
||
}
|
||
const obj = {
|
||
path: mod.routes.detail,
|
||
query: { priceSetId: form.priceSetId }
|
||
}
|
||
if (proxy.$tab) {
|
||
proxy.$tab.closeOpenPage(obj)
|
||
} else {
|
||
proxy.$router.push(obj)
|
||
}
|
||
return
|
||
}
|
||
if (showBasic.value) {
|
||
const validBasic = await basicRef.value.validate()
|
||
if (!validBasic) return
|
||
}
|
||
if (!form.version.priceBuildMode) {
|
||
proxy.$modal.msgWarning('请先选择创建价格方式')
|
||
return
|
||
}
|
||
const detailRows = (form.details || []).filter(d => d && d.productCode)
|
||
if (!detailRows.length) {
|
||
proxy.$modal.msgWarning('请先拉取产品或导入 Excel 建价')
|
||
return
|
||
}
|
||
creating.value = true
|
||
try {
|
||
if (newVersionMode.value && form.version.psvId == null && form.priceSetId) {
|
||
const verRes = await mod.api.createPriceSetVersion(form.priceSetId)
|
||
if (verRes.data != null) {
|
||
form.version.psvId = verRes.data
|
||
}
|
||
}
|
||
const payload = buildPayload({ forceDetails: true })
|
||
const priceSetId = await runSaveWithProgress(payload)
|
||
form.priceSetId = priceSetId
|
||
proxy.$modal.msgSuccess(mod.labels.savedSuccess)
|
||
const obj = {
|
||
path: mod.routes.detail,
|
||
query: { priceSetId }
|
||
}
|
||
if (proxy.$tab) {
|
||
proxy.$tab.closeOpenPage(obj)
|
||
} else {
|
||
proxy.$router.push(obj)
|
||
}
|
||
} catch (e) {
|
||
// surfaced
|
||
} finally {
|
||
creating.value = false
|
||
}
|
||
}
|
||
|
||
function buildPayload(opts = {}) {
|
||
const { basic, version } = form
|
||
const detailRows = form.details.filter(d => d && d.productCode)
|
||
// Edit live-save: omit details when all rows already have psdId.
|
||
// Create finish: forceDetails sends all rows (psdId cleared) for insert.
|
||
const hasUnsavedDetail = detailRows.some(d => d.psdId == null)
|
||
const payload = {
|
||
priceSetId: form.priceSetId,
|
||
priceSetName: basic.priceSetName,
|
||
setType: 'FIXED',
|
||
defaultDiscount: undefined,
|
||
priceSetNote: basic.priceSetNote,
|
||
version: {
|
||
psvId: (copyMode.value || (newVersionMode.value && version.psvId == null)) ? undefined : version.psvId,
|
||
versionNo: version.versionNo,
|
||
priceBuildMode: version.priceBuildMode || undefined
|
||
}
|
||
}
|
||
if (draftPefIds.value.length) {
|
||
payload.pefIds = draftPefIds.value.slice()
|
||
} else if (draftPefId.value != null) {
|
||
payload.pefId = draftPefId.value
|
||
}
|
||
if (opts.forceDetails || hasUnsavedDetail) {
|
||
payload.details = detailRows.map(d => {
|
||
const p = toDetailPayload(d)
|
||
if (opts.forceDetails) {
|
||
delete p.psdId
|
||
delete p.pspId
|
||
delete p.priceSetId
|
||
delete p.psvId
|
||
}
|
||
return p
|
||
})
|
||
}
|
||
const customers = form.customers.filter(c => c && c.customerCode).map(toCustomerPayload)
|
||
if (customers.length) {
|
||
payload.customers = customers
|
||
}
|
||
return payload
|
||
}
|
||
|
||
/**
|
||
* New-version flow creates an empty draft as soon as user clicks "新建价格版本".
|
||
* If user cancels without finishing (no build mode / no details on server), remove that orphan draft.
|
||
*/
|
||
async function tryDeleteEmptyNewVersionDraft() {
|
||
if (!newVersionMode.value || !form.priceSetId || form.version.psvId == null) return
|
||
const psvId = form.version.psvId
|
||
try {
|
||
const res = await mod.api.getPriceSet(form.priceSetId)
|
||
const versions = (res.data || {}).versions || []
|
||
const v = versions.find(x => x && String(x.psvId) === String(psvId))
|
||
if (!v || String(v.status) !== '0') return
|
||
const noMode = !v.priceBuildMode
|
||
const noDetails = !(Number(v.detailCount) > 0) && !(v.details || []).length
|
||
if (noMode && noDetails) {
|
||
await mod.api.deletePriceSetVersion(psvId)
|
||
}
|
||
} catch (e) {
|
||
// ignore cleanup errors; still navigate back
|
||
}
|
||
}
|
||
|
||
async function goBack() {
|
||
await tryDeleteEmptyNewVersionDraft()
|
||
const obj = (newVersionMode.value || copyMode.value) && form.priceSetId
|
||
? { path: mod.routes.detail, query: { priceSetId: form.priceSetId } }
|
||
: { path: mod.routes.list }
|
||
if (proxy.$tab) {
|
||
proxy.$tab.closeOpenPage(obj)
|
||
} else {
|
||
proxy.$router.push(obj)
|
||
}
|
||
}
|
||
|
||
function onPsvIdUpdate(psvId) {
|
||
if (psvId != null && psvId !== '') form.version.psvId = psvId
|
||
}
|
||
|
||
function onPefIdUpdate(pefId) {
|
||
draftPefId.value = pefId
|
||
if (pefId == null || pefId === '') return
|
||
const id = String(pefId)
|
||
if (!draftPefIds.value.some(x => String(x) === id)) {
|
||
draftPefIds.value.push(pefId)
|
||
}
|
||
}
|
||
|
||
/** Step2 chose PULL / IMPORT / IMPORT_FRESH: edit live-saves mode; create only local + optional preview pull. */
|
||
function onPriceBuildModeUpdate(mode) {
|
||
if (!mode) return
|
||
if (isBuildModeLockedVersion(form.version.status)) return
|
||
if (mode === form.version.priceBuildMode) return
|
||
const leavingSaved = livePersist.value
|
||
&& String(form.version.priceBuildMode || '') === String(savedPriceBuildMode.value || '')
|
||
if (leavingSaved) {
|
||
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
|
||
}
|
||
form.version.priceBuildMode = mode
|
||
if (mode !== 'IMPORT' && mode !== 'IMPORT_FRESH') {
|
||
draftPefId.value = undefined
|
||
draftPefIds.value = []
|
||
}
|
||
form.details.splice(0, form.details.length)
|
||
if (detailsRef.value && detailsRef.value.clearLocalDraft) {
|
||
detailsRef.value.clearLocalDraft()
|
||
}
|
||
if (livePersist.value) {
|
||
const backToSaved = String(mode || '') === String(savedPriceBuildMode.value || '')
|
||
if (backToSaved) {
|
||
form.products.splice(0, form.products.length, ...(savedProductsSnapshot.value || []).map(p => ({ ...p })))
|
||
if (!form.products.length && form.priceSetId) {
|
||
reloadSavedProductsForCreate()
|
||
} else {
|
||
proxy.$modal.msgSuccess("已恢复原建价方式及产品列表(未保存,库数据未变)")
|
||
}
|
||
return
|
||
}
|
||
form.products.splice(0, form.products.length)
|
||
proxy.$modal.msgSuccess("已切换建价方式(本地),请点「完成」保存后再拉取或导入;切回原方式可恢复列表")
|
||
return
|
||
}
|
||
form.products.splice(0, form.products.length)
|
||
if (mode === 'PULL' && detailsRef.value && detailsRef.value.pullAllFromLibrary) {
|
||
nextTick(() => {
|
||
detailsRef.value.pullAllFromLibrary({ silent: true }).then(() => {
|
||
proxy.$modal.msgSuccess("已拉取产品列表,可继续编辑价格")
|
||
}).catch(() => {})
|
||
})
|
||
} else if (mode === 'IMPORT' && showImportInherit.value) {
|
||
nextTick(async () => {
|
||
// newVersion/copy: wait until loadExisting fills priceSetId
|
||
const qid = route.query.priceSetId
|
||
if (!form.priceSetId && qid) {
|
||
for (let i = 0; i < 50 && !form.priceSetId; i++) {
|
||
await new Promise(r => setTimeout(r, 100))
|
||
}
|
||
}
|
||
const loader = detailsRef.value && detailsRef.value.loadLatestPriorVersionDraft
|
||
if (!loader) {
|
||
proxy.$modal.msgSuccess("请导入 Excel 建价")
|
||
return
|
||
}
|
||
if (!form.priceSetId) {
|
||
proxy.$modal.msgSuccess("请直接导入 Excel 建价")
|
||
return
|
||
}
|
||
try {
|
||
const ok = await loader()
|
||
proxy.$modal.msgSuccess(ok
|
||
? "已带出上一版全部产品,导入 Excel 按产品+国家覆盖或新增"
|
||
: "未找到含产品的上一版,请导入 Excel 建价")
|
||
} catch (e) {
|
||
proxy.$modal.msgSuccess("请导入 Excel 建价")
|
||
}
|
||
})
|
||
} else if (mode === 'IMPORT_FRESH') {
|
||
proxy.$modal.msgSuccess("请直接导入 Excel 建价(不延用以前的产品价格)")
|
||
}
|
||
}
|
||
|
||
function reloadSavedProductsForCreate() {
|
||
mod.api.getPriceSet(form.priceSetId).then(res => {
|
||
const data = res.data || {}
|
||
const versions = data.versions || []
|
||
const qPsv = form.version.psvId
|
||
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
||
const current = byQuery || data.version || versions[0] || {}
|
||
const list = (current.products || []).map(p => ({ ...p }))
|
||
form.products.splice(0, form.products.length, ...list)
|
||
savedProductsSnapshot.value = list.map(p => ({ ...p }))
|
||
proxy.$modal.msgSuccess("已恢复原建价方式及产品列表(未保存,库数据未变)")
|
||
})
|
||
}
|
||
|
||
async function refreshProductsFromCurrentVersion() {
|
||
if (!form.priceSetId) return
|
||
const res = await mod.api.getPriceSet(form.priceSetId)
|
||
const data = res.data || {}
|
||
const versions = data.versions || []
|
||
const qPsv = form.version.psvId
|
||
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
||
const current = byQuery || data.version || versions[0] || {}
|
||
const list = (current.products || []).map(p => ({ ...p }))
|
||
form.products.splice(0, form.products.length, ...list)
|
||
form.details.splice(0, form.details.length)
|
||
savedProductsSnapshot.value = list.map(p => ({ ...p }))
|
||
}
|
||
|
||
function applySetData(data, asCopy, asNewVersion) {
|
||
form.priceSetId = data.priceSetId
|
||
Object.assign(form.basic, {
|
||
priceSetName: data.priceSetName,
|
||
setType: 'FIXED',
|
||
defaultDiscount: data.defaultDiscount != null ? Number(data.defaultDiscount) : 88,
|
||
priceSetNote: data.priceSetNote
|
||
})
|
||
const versions = data.versions || []
|
||
const qPsv = route.query.psvId
|
||
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
||
const fromData = (data.version && data.version.psvId) ? data.version : null
|
||
const current = byQuery || fromData || versions[0] || {}
|
||
if (asCopy) {
|
||
const keptMode = form.version.priceBuildMode
|
||
const keptProducts = keptMode ? form.products.slice() : []
|
||
const keptDetails = keptMode ? form.details.slice() : []
|
||
form.version.psvId = undefined
|
||
form.version.versionNo = nextVersionNo(versions)
|
||
form.version.effectiveTime = undefined
|
||
form.version.expireTime = undefined
|
||
form.version.status = undefined
|
||
form.version.priceBuildMode = keptMode || undefined
|
||
draftPefId.value = undefined
|
||
draftPefIds.value = []
|
||
savedPriceBuildMode.value = undefined
|
||
if (keptMode) {
|
||
form.products.splice(0, form.products.length, ...keptProducts)
|
||
form.details.splice(0, form.details.length, ...keptDetails)
|
||
} else {
|
||
form.details = []
|
||
form.products = []
|
||
}
|
||
} else if (asNewVersion) {
|
||
// Draft is created on finish only. Resume only when query has psvId (legacy empty draft).
|
||
// Keep mode/products if user already chose a build mode before this response landed.
|
||
const keptMode = form.version.priceBuildMode
|
||
const keptProducts = keptMode ? form.products.slice() : []
|
||
const keptDetails = keptMode ? form.details.slice() : []
|
||
const resume = qPsv != null ? byQuery : null
|
||
form.version.psvId = resume ? resume.psvId : undefined
|
||
form.version.versionNo = (resume && resume.versionNo) || nextVersionNo(versions)
|
||
form.version.effectiveTime = undefined
|
||
form.version.expireTime = undefined
|
||
form.version.status = (resume && resume.status) || '0'
|
||
form.version.priceBuildMode = keptMode || undefined
|
||
draftPefId.value = undefined
|
||
draftPefIds.value = []
|
||
savedPriceBuildMode.value = undefined
|
||
if (keptMode) {
|
||
form.products.splice(0, form.products.length, ...keptProducts)
|
||
form.details.splice(0, form.details.length, ...keptDetails)
|
||
} else {
|
||
form.details = []
|
||
form.products = []
|
||
}
|
||
} else {
|
||
form.version.psvId = current.psvId
|
||
form.version.versionNo = current.versionNo || nextVersionNo(versions)
|
||
form.version.effectiveTime = current.effectiveTime
|
||
form.version.expireTime = current.expireTime
|
||
form.version.status = current.status
|
||
form.version.priceBuildMode = current.priceBuildMode || undefined
|
||
draftPefId.value = undefined
|
||
draftPefIds.value = []
|
||
savedPriceBuildMode.value = current.priceBuildMode || undefined
|
||
form.details = []
|
||
form.products = (current.products || []).map(p => ({ ...p }))
|
||
savedProductsSnapshot.value = form.products.map(p => ({ ...p }))
|
||
}
|
||
form.customers = (data.customers || []).map(c => ({ ...c }))
|
||
}
|
||
|
||
function loadExisting(id, asCopy, asNewVersion) {
|
||
pageReady.value = false
|
||
return mod.api.getPriceSet(id).then(res => {
|
||
applySetData(res.data || {}, asCopy, asNewVersion)
|
||
}).finally(() => {
|
||
pageReady.value = true
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
refData.loadProducts().finally(() => {
|
||
const id = route.query.priceSetId
|
||
if (id) {
|
||
loadExisting(id, copyMode.value, newVersionMode.value)
|
||
} else {
|
||
pageReady.value = true
|
||
}
|
||
})
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.create-section {
|
||
margin-bottom: 16px;
|
||
}
|
||
.wizard-actions {
|
||
text-align: center;
|
||
padding: 8px 0 24px;
|
||
}
|
||
.save-progress__msg {
|
||
margin: 12px 0 4px;
|
||
color: #606266;
|
||
font-size: 13px;
|
||
word-break: break-all;
|
||
}
|
||
.save-progress__hint {
|
||
margin: 0;
|
||
color: #909399;
|
||
font-size: 12px;
|
||
}
|
||
</style>
|