费用配置、菜单调整

master
lzq 2026-02-02 18:05:41 +08:00
parent a51f166897
commit f502aca92b
53 changed files with 1542 additions and 296 deletions

View File

@ -33,7 +33,7 @@ const selectRows = Utils.resetAble(reactive<string[]>([]))
function paging() { function paging() {
searching.value = true searching.value = true
return props.loader(searchForm) return props.loader(searchForm.$clone())
.then(res => { .then(res => {
totalCount.value = res.total ?? 0 totalCount.value = res.total ?? 0
const value = res.records ?? [] const value = res.records ?? []

View File

@ -120,7 +120,7 @@ interface FormPropsType<P, T extends DefaultRow> {
*/ */
labelWidth: number, labelWidth: number,
/** /**
* px190px * px200px
*/ */
contentWidth: number, contentWidth: number,
/** /**
@ -135,7 +135,25 @@ interface FormPropsType<P, T extends DefaultRow> {
/** /**
* *
*/ */
simpleForm: Partial<FormProps> simpleForm: Partial<FormProps> & {
/**
* 1
*/
colCount: number,
/**
* px200px
*/
contentWidth: number,
/**
* px10px
*/
// rgap: number,
/**
* px10px
*/
// cgap: number
}
} }
/** /**
@ -180,15 +198,28 @@ function buildSearchForm<P, T extends DefaultRow>(searchForm: DeepPartial<FormPr
if (searchForm.highForm == null) { if (searchForm.highForm == null) {
searchForm.highForm = { searchForm.highForm = {
labelWidth: 90, labelWidth: 90,
contentWidth: 190, contentWidth: 200,
rgap: 10, rgap: 10,
cgap: 10, cgap: 10,
} }
} }
if (searchForm.highForm.labelWidth == null) searchForm.highForm.labelWidth = 90 if (searchForm.highForm.labelWidth == null) searchForm.highForm.labelWidth = 90
if (searchForm.highForm.contentWidth == null) searchForm.highForm.contentWidth = 90 if (searchForm.highForm.contentWidth == null) searchForm.highForm.contentWidth = 200
if (searchForm.highForm.rgap == null) searchForm.highForm.rgap = 10 if (searchForm.highForm.rgap == null) searchForm.highForm.rgap = 10
if (searchForm.highForm.cgap == null) searchForm.highForm.cgap = 10 if (searchForm.highForm.cgap == null) searchForm.highForm.cgap = 10
if (searchForm.simpleForm == null) {
searchForm.simpleForm = {
contentWidth: 200,
colCount: 1,
// rgap: 10,
// cgap: 10,
}
}
if (searchForm.simpleForm.colCount == null) searchForm.simpleForm.colCount = 1
if (searchForm.simpleForm.contentWidth == null) searchForm.simpleForm.contentWidth = 200
// if (searchForm.simpleForm.rgap == null) searchForm.simpleForm.rgap = 10
// if (searchForm.simpleForm.cgap == null) searchForm.simpleForm.cgap = 10
return searchForm return searchForm
} }
@ -350,6 +381,19 @@ const component = defineComponent(
} }
}) })
const toolBarRightCssParam = computed(() => {
const colCount = props.searchForm.simpleForm.colCount
const contentWidth = props.searchForm.simpleForm.contentWidth
const btnCount = props.toolBar.rightTools.length + (props.searchForm.export == null ? 0 : 1) + (props.pageLayout.enableHighForm ? 1 : 0)
return {
'--form-item-count': colCount,
'--form-item-width': contentWidth + 'px',
// '--rgap': props.searchForm.simpleForm.rgap + 'px',
// '--cgap': props.searchForm.simpleForm.cgap + 'px',
'--btn-count': btnCount,
}
})
const pageCssParam = computed(() => { const pageCssParam = computed(() => {
const dataListHeight = props.pageLayout.dataListHeight const dataListHeight = props.pageLayout.dataListHeight
const searchFormHeight = props.pageLayout.searchFormHeight const searchFormHeight = props.pageLayout.searchFormHeight
@ -520,7 +564,7 @@ const component = defineComponent(
)) ))
} }
</div> </div>
<div class={styles.toolBarRight}> <div class={styles.toolBarRight} style={toolBarRightCssParam.value}>
<ElScrollbar> <ElScrollbar>
{/*@ts-ignore*/} {/*@ts-ignore*/}
<ElForm onSubmit={withModifiers(doSearch, [ 'prevent' ])}> <ElForm onSubmit={withModifiers(doSearch, [ 'prevent' ])}>

View File

@ -53,7 +53,7 @@
box-sizing: border-box; box-sizing: border-box;
display grid display grid
grid-template-columns 1fr grid-template-columns 1fr
grid-template-rows 50px minmax(0, 1fr) 50px grid-template-rows 32px minmax(0, 1fr) 50px
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
padding: 15px 20px 20px 15px; padding: 15px 20px 20px 15px;
border-radius: 8px; border-radius: 8px;
@ -64,8 +64,7 @@
.tool-bar { .tool-bar {
display: grid; display: grid;
grid-template-columns: minmax(260px, 1fr) minmax(428px, 1fr); grid-template-columns: 1fr auto;
grid-template-rows: 50px;
box-sizing: border-box; box-sizing: border-box;
.tool-bar-left { .tool-bar-left {
@ -74,16 +73,28 @@
.tool-bar-right { .tool-bar-right {
display grid display grid
grid-template-columns: minmax(200px, 1fr) auto; justify-self end
grid-auto-rows: 32px; grid-template-columns: auto calc(var(--btn-count) * 42px + 64px);
grid-template-rows: 32px;
gap 10px gap 10px
& > div:first-child { & > div:first-child {
justify-self: end; justify-self: end;
//width 100%;
//width calc(var(--form-item-count) * var(--form-item-width) * 1px + var(--form-item-count) * 10px) ;
/*:global(.el-scrollbar__wrap) {
width 100%;
}
:global(.el-scrollbar__view) {
width 100%;
}*/
:global(.el-form) { :global(.el-form) {
//width 100%;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); grid-template-columns: repeat(var(--form-item-count), var(--form-item-width));
gap 10px gap 10px
:global(.el-form-item) { :global(.el-form-item) {

View File

@ -5,6 +5,7 @@
// ------ // ------
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
export {} export {}

View File

@ -100,6 +100,7 @@ onUnmounted(() => {
& > div:nth-child(1) { & > div:nth-child(1) {
height: 100%; height: 100%;
min-width 300px;
display: flex; display: flex;
align-items: center; align-items: center;
box-sizing border-box box-sizing border-box
@ -118,8 +119,10 @@ onUnmounted(() => {
& > div:nth-child(2) { & > div:nth-child(2) {
height: 100%; height: 100%;
min-width 400px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content end
box-sizing border-box box-sizing border-box
gap 20px gap 20px

View File

@ -0,0 +1,112 @@
<template>
<ATablePage
ref="tablePage"
v-bind="tablePageProps">
<template #simpleFormItem="formData">
<ElFormItem>
<ElInput v-model="formData.customerId" placeholder="管理员名称"/>
</ElFormItem>
<ElFormItem>
<ElInput v-model="formData.orgId" placeholder="企业名称"/>
</ElFormItem>
<ElFormItem>
<ElInput v-model="formData.expenseItemName" placeholder="费用名称"/>
</ElFormItem>
</template>
<template #columns>
<ElTableColumn label="管理员名称" prop="userId"/>
<ElTableColumn label="企业名称" prop="customerId"/>
<ElTableColumn label="费用类型" prop="expenseItemCategory"/>
<ElTableColumn label="付费项名称" prop="expenseItemName"/>
<ElTableColumn label="计费策略" prop="expenseStrategy"/>
<ElTableColumn label="计量单位" prop="unit"/>
<ElTableColumn label="税率" prop="taxRate"/>
<ElTableColumn label="单价" prop="unitPrice"/>
<ElTableColumn label="起步价" prop="initialPrice"/>
<ElTableColumn label="起步量" prop="initialQuantity"/>
<ElTableColumn label="每档的量" prop="everyQuantity"/>
<ElTableColumn label="是否可用" prop="canuse"/>
<ElTableColumn label="备注" prop="memo"/>
</template>
<ExpenseItemForm ref="expenseItemForm" :research="research"/>
<ExpenseItemDetail ref="expenseItemDetail"/>
</ATablePage>
</template>
<script lang="ts" setup>
import ExpenseItemApi from '@/pages/fin/expense-item/expense-item-api.ts'
import ExpenseItemForm from '@/pages/fin/expense-item/ExpenseItemForm.vue'
import ExpenseItemDetail from '@/pages/fin/expense-item/ExpenseItemDetail.vue'
import ATablePage, {
type ATablePageInstance,
buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx'
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const expenseItemFormIns = useTemplateRef<InstanceType<typeof ExpenseItemForm>>('expenseItemForm')
const expenseItemDetailIns = useTemplateRef<InstanceType<typeof ExpenseItemDetail>>('expenseItemDetail')
function research() {
tablePageIns.value?.doSearch()
}
const tablePageProps = buildTablePageProps<ExpenseItemTypes.SearchExpenseItemParam, ExpenseItemTypes.SearchExpenseItemResult>({
pageLayout: {
enableHighForm: false,
},
searchForm: {
paging: ExpenseItemApi.paging,
},
toolBar: {
leftTools: [
{
icon: 'Plus',
label: '新建',
action() {
expenseItemFormIns.value?.open()
},
},
],
},
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'info',
action({row}) {
expenseItemDetailIns.value?.open(row)
},
},
{
tooltip: '编辑',
icon: 'Edit',
type: 'primary',
action({row}) {
expenseItemFormIns.value?.open(row)
},
},
{
icon: 'Delete',
loading: false,
type: 'danger',
tooltip: '删除',
confirm: {
title: '是否删除当前数据',
},
action({row}) {
return ExpenseItemApi.del([ row.id! ])
.then(() => {
ElMessage.success('删除成功')
return true
})
},
},
],
},
},
})
</script>

View File

@ -0,0 +1,93 @@
<template>
<ADetailPanel
ref="detailPanel"
v-bind="detailPanelProps">
<template #default="detailData">
<ElDescriptions border title="收费项目">
<ElDescriptionsItem label="Id" prop="id">
{{ detailData.id }}
</ElDescriptionsItem>
<ElDescriptionsItem label="归属用户 Id" prop="userId">
{{ detailData.userId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="归属客户 Id" prop="customerId">
{{ detailData.customerId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="归属组织 Id" prop="orgId">
{{ detailData.orgId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="产品 Id仅当项目类型为“产品”时有值" prop="goodsId">
{{ detailData.goodsId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费" prop="expenseItemCategory">
{{ detailData.expenseItemCategory }}
</ElDescriptionsItem>
<ElDescriptionsItem label="付费项名称" prop="expenseItemName">
{{ detailData.expenseItemName }}
</ElDescriptionsItem>
<ElDescriptionsItem label="计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价" prop="expenseStrategy">
{{ detailData.expenseStrategy }}
</ElDescriptionsItem>
<ElDescriptionsItem label="计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨" prop="unit">
{{ detailData.unit }}
</ElDescriptionsItem>
<ElDescriptionsItem label="税率" prop="taxRate">
{{ detailData.taxRate }}
</ElDescriptionsItem>
<ElDescriptionsItem label="单价;单位:元,弹性模式-->每档价格" prop="unitPrice">
{{ detailData.unitPrice }}
</ElDescriptionsItem>
<ElDescriptionsItem label="起步价;单位:元,<= 起步量 固定费用" prop="initialPrice">
{{ detailData.initialPrice }}
</ElDescriptionsItem>
<ElDescriptionsItem label="起步量" prop="initialQuantity">
{{ detailData.initialQuantity }}
</ElDescriptionsItem>
<ElDescriptionsItem label="每档的量" prop="everyQuantity">
{{ detailData.everyQuantity }}
</ElDescriptionsItem>
<ElDescriptionsItem label="是否可用0-->否、1-->是" prop="canuse">
{{ detailData.canuse }}
</ElDescriptionsItem>
<ElDescriptionsItem label="备注" prop="memo">
{{ detailData.memo }}
</ElDescriptionsItem>
<ElDescriptionsItem label="创建人 Id sys_user.id" prop="creatorId">
{{ detailData.creatorId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改人 Id sys_user.id" prop="modifierId">
{{ detailData.modifierId }}
</ElDescriptionsItem>
<ElDescriptionsItem label="创建时间" prop="createTime">
{{ detailData.createTime }}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改时间" prop="modifyTime">
{{ detailData.modifyTime }}
</ElDescriptionsItem>
<ElDescriptionsItem label="是否删除; 0-->未删除、1-->已删除" prop="deleted">
{{ detailData.deleted }}
</ElDescriptionsItem>
</ElDescriptions>
</template>
</ADetailPanel>
</template>
<script lang="ts" setup>
import ExpenseItemApi from '@/pages/fin/expense-item/expense-item-api.ts'
import ADetailPanel, {
type ADetailPanelInstance,
buildDetailPanelProps,
} from '@/components/a-detail-panel/ADetailPanel.tsx'
const detailPanelIns = useTemplateRef<ADetailPanelInstance<ExpenseItemTypes.SearchExpenseItemResult>>('detailPanel')
const detailPanelProps = buildDetailPanelProps<ExpenseItemTypes.SearchExpenseItemResult>({
title: '收费项目详情',
detailsLoader: ExpenseItemApi.detail,
})
defineExpose({
open(data: ExpenseItemTypes.SearchExpenseItemResult) {
detailPanelIns.value?.open(data)
},
})
</script>

View File

@ -0,0 +1,152 @@
<template>
<AFormPanel
ref="formPanel"
v-bind="formPanelProps">
<template #default="formData">
<div class="form-items">
<ElFormItem label="Id" prop="id">
<ElInput v-model="formData.id" placeholder="Id"/>
</ElFormItem>
<ElFormItem label="归属用户 Id" prop="userId">
<ElInput v-model="formData.userId" placeholder="归属用户 Id"/>
</ElFormItem>
<ElFormItem label="归属客户 Id" prop="customerId">
<ElInput v-model="formData.customerId" placeholder="归属客户 Id"/>
</ElFormItem>
<ElFormItem label="归属组织 Id" prop="orgId">
<ElInput v-model="formData.orgId" placeholder="归属组织 Id"/>
</ElFormItem>
<ElFormItem label="产品 Id仅当项目类型为“产品”时有值" prop="goodsId">
<ElInput v-model="formData.goodsId" placeholder="产品 Id仅当项目类型为“产品”时有值"/>
</ElFormItem>
<ElFormItem label="收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费" prop="expenseItemCategory">
<ElInput v-model="formData.expenseItemCategory" placeholder="收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费"/>
</ElFormItem>
<ElFormItem label="付费项名称" prop="expenseItemName">
<ElInput v-model="formData.expenseItemName" placeholder="付费项名称"/>
</ElFormItem>
<ElFormItem label="计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价" prop="expenseStrategy">
<ElInput v-model="formData.expenseStrategy" placeholder="计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价"/>
</ElFormItem>
<ElFormItem label="计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨" prop="unit">
<ElInput v-model="formData.unit" placeholder="计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨"/>
</ElFormItem>
<ElFormItem label="税率" prop="taxRate">
<ElInput v-model="formData.taxRate" placeholder="税率"/>
</ElFormItem>
<ElFormItem label="单价;单位:元,弹性模式-->每档价格" prop="unitPrice">
<ElInput v-model="formData.unitPrice" placeholder="单价;单位:元,弹性模式-->每档价格"/>
</ElFormItem>
<ElFormItem label="起步价;单位:元,<= 起步量 固定费用" prop="initialPrice">
<ElInput v-model="formData.initialPrice" placeholder="起步价;单位:元,<= 起步量 固定费用"/>
</ElFormItem>
<ElFormItem label="起步量" prop="initialQuantity">
<ElInput v-model="formData.initialQuantity" placeholder="起步量"/>
</ElFormItem>
<ElFormItem label="每档的量" prop="everyQuantity">
<ElInput v-model="formData.everyQuantity" placeholder="每档的量"/>
</ElFormItem>
<ElFormItem label="是否可用0-->否、1-->是" prop="canuse">
<ElInput v-model="formData.canuse" placeholder="是否可用0-->否、1-->是"/>
</ElFormItem>
<ElFormItem label="备注" prop="memo">
<ElInput v-model="formData.memo" placeholder="备注"/>
</ElFormItem>
<ElFormItem label="创建人 Id sys_user.id" prop="creatorId">
<ElInput v-model="formData.creatorId" placeholder="创建人 Id sys_user.id"/>
</ElFormItem>
<ElFormItem label="修改人 Id sys_user.id" prop="modifierId">
<ElInput v-model="formData.modifierId" placeholder="修改人 Id sys_user.id"/>
</ElFormItem>
<ElFormItem label="创建时间" prop="createTime">
<ElInput v-model="formData.createTime" placeholder="创建时间"/>
</ElFormItem>
<ElFormItem label="修改时间" prop="modifyTime">
<ElInput v-model="formData.modifyTime" placeholder="修改时间"/>
</ElFormItem>
<ElFormItem label="是否删除; 0-->未删除、1-->已删除" prop="deleted">
<ElInput v-model="formData.deleted" placeholder="是否删除; 0-->未删除、1-->已删除"/>
</ElFormItem>
</div>
</template>
</AFormPanel>
</template>
<script lang="ts" setup>
import ExpenseItemApi from '@/pages/fin/expense-item/expense-item-api.ts'
import AFormPanel, {
type AFormPanelInstance,
buildFormPanelProps,
} from '@/components/a-form-panel/AFormPanel.tsx'
import Strings from '@/common/utils/strings.ts'
const props = withDefaults(defineProps<{
research?: () => void
}>(), {
research: () => {
},
})
const formPanelIns = useTemplateRef<AFormPanelInstance>('formPanel')
const status = ref<'add' | 'modify'>('add')
const formPanelProps = buildFormPanelProps<ExpenseItemTypes.SearchExpenseItemResult>({
title: status.value === 'add' ? '新建收费项目' : '修改收费项目信息',
detailsLoader(id?: string) {
if (Strings.isBlank(id)) {
status.value = 'add'
return Promise.resolve()
} else {
status.value = 'modify'
return ExpenseItemApi
.detail(id!)
.then(res => res.data)
}
},
doSubmit(data) {
if (status.value === 'add') {
return ExpenseItemApi
.add(data)
.then(props.research)
} else {
return ExpenseItemApi
.modify(data)
.then(props.research)
}
},
rules: {
id: [ {required: true, message: '请填写Id', trigger: 'blur'} ],
userId: [ {required: true, message: '请填写归属用户 Id', trigger: 'blur'} ],
customerId: [ {required: true, message: '请填写归属客户 Id', trigger: 'blur'} ],
orgId: [ {required: true, message: '请填写归属组织 Id', trigger: 'blur'} ],
goodsId: [ {required: true, message: '请填写产品 Id仅当项目类型为“产品”时有值', trigger: 'blur'} ],
expenseItemCategory: [ {required: true, message: '请填写收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费', trigger: 'blur'} ],
expenseItemName: [ {required: true, message: '请填写付费项名称', trigger: 'blur'} ],
expenseStrategy: [ {required: true, message: '请填写计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价', trigger: 'blur'} ],
unit: [ {required: true, message: '请填写计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨', trigger: 'blur'} ],
taxRate: [ {required: true, message: '请填写税率', trigger: 'blur'} ],
unitPrice: [ {required: true, message: '请填写单价;单位:元,弹性模式-->每档价格', trigger: 'blur'} ],
initialPrice: [ {required: true, message: '请填写起步价;单位:元,<= 起步量 固定费用', trigger: 'blur'} ],
initialQuantity: [ {required: true, message: '请填写起步量', trigger: 'blur'} ],
everyQuantity: [ {required: true, message: '请填写每档的量', trigger: 'blur'} ],
canuse: [ {required: true, message: '请填写是否可用0-->否、1-->是', trigger: 'blur'} ],
memo: [ {required: true, message: '请填写备注', trigger: 'blur'} ],
creatorId: [ {required: true, message: '请填写创建人 Id sys_user.id', trigger: 'blur'} ],
modifierId: [ {required: true, message: '请填写修改人 Id sys_user.id', trigger: 'blur'} ],
createTime: [ {required: true, message: '请填写创建时间', trigger: 'blur'} ],
modifyTime: [ {required: true, message: '请填写修改时间', trigger: 'blur'} ],
deleted: [ {required: true, message: '请填写是否删除; 0-->未删除、1-->已删除', trigger: 'blur'} ],
},
})
defineExpose({
open(data?: ExpenseItemTypes.SearchExpenseItemResult) {
formPanelIns.value?.open(data?.id)
},
})
</script>
<style lang="stylus" scoped>
.form-items {
grid-template-columns: 1fr 1fr;
}
</style>

View File

@ -0,0 +1,22 @@
import {
get,
post,
} from '@/common/utils/http-util.ts'
export default {
paging(data: ExpenseItemTypes.SearchExpenseItemParam) {
return get<G.PageResult<ExpenseItemTypes.SearchExpenseItemResult>>('/expense_item/paging', data)
},
detail(id: string) {
return get<ExpenseItemTypes.SearchExpenseItemResult>('/expense_item/detail', {id})
},
add(data: ExpenseItemTypes.AddExpenseItemParam) {
return post('/expense_item/add', data)
},
modify(data: ExpenseItemTypes.ModifyExpenseItemParam) {
return post('/expense_item/modify', data)
},
del(ids: string[]) {
return post('/expense_item/del', ids)
},
}

View File

@ -0,0 +1,185 @@
export {}
declare global {
namespace ExpenseItemTypes {
interface SearchExpenseItemParam extends G.PageParam {
// Id
id?: string
// 归属用户 Id
userId?: string
// 归属客户 Id
customerId?: string
// 归属组织 Id
orgId?: string
// 产品 Id仅当项目类型为“产品”时有值
goodsId?: string
// 收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费
expenseItemCategory?: string
// 付费项名称
expenseItemName?: string
// 计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价
expenseStrategy?: string
// 计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨
unit?: string
// 税率
taxRate?: string
// 单价;单位:元,弹性模式-->每档价格
unitPrice?: string
// 起步价;单位:元,<= 起步量 固定费用
initialPrice?: string
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Id sys_user.id
creatorId?: string
// 修改人 Id sys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
}
interface SearchExpenseItemResult {
// Id
id?: string
// 归属用户 Id
userId?: string
// 归属客户 Id
customerId?: string
// 归属组织 Id
orgId?: string
// 产品 Id仅当项目类型为“产品”时有值
goodsId?: string
// 收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费
expenseItemCategory?: string
// 付费项名称
expenseItemName?: string
// 计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价
expenseStrategy?: string
// 计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨
unit?: string
// 税率
taxRate?: string
// 单价;单位:元,弹性模式-->每档价格
unitPrice?: string
// 起步价;单位:元,<= 起步量 固定费用
initialPrice?: string
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Id sys_user.id
creatorId?: string
// 修改人 Id sys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
}
interface AddExpenseItemParam {
// Id
id?: string
// 归属用户 Id
userId?: string
// 归属客户 Id
customerId?: string
// 归属组织 Id
orgId?: string
// 产品 Id仅当项目类型为“产品”时有值
goodsId?: string
// 收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费
expenseItemCategory?: string
// 付费项名称
expenseItemName?: string
// 计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价
expenseStrategy?: string
// 计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨
unit?: string
// 税率
taxRate?: string
// 单价;单位:元,弹性模式-->每档价格
unitPrice?: string
// 起步价;单位:元,<= 起步量 固定费用
initialPrice?: string
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Id sys_user.id
creatorId?: string
// 修改人 Id sys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
}
interface ModifyExpenseItemParam {
// Id
id?: string
// 归属用户 Id
userId?: string
// 归属客户 Id
customerId?: string
// 归属组织 Id
orgId?: string
// 产品 Id仅当项目类型为“产品”时有值
goodsId?: string
// 收费项目类型QiTa-->其他、ChanPin-->产品、YunFei-->运费
expenseItemCategory?: string
// 付费项名称
expenseItemName?: string
// 计费策略字典代码expense_strategyMianFei-->免费、TanXing-->弹性、GuDing-->固定、DanJia-->单价
expenseStrategy?: string
// 计量单位字典代码unitChe-->车、Fang-->方、GongLi-->公里、Dun-->吨
unit?: string
// 税率
taxRate?: string
// 单价;单位:元,弹性模式-->每档价格
unitPrice?: string
// 起步价;单位:元,<= 起步量 固定费用
initialPrice?: string
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Id sys_user.id
creatorId?: string
// 修改人 Id sys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
}
}
}

View File

@ -0,0 +1,3 @@
export default {
component: () => import('@/pages/fin/expense-item/ExpenseItem.vue'),
} as RouterTypes.RouteConfig

View File

@ -3,11 +3,6 @@
ref="tablePage" ref="tablePage"
v-bind="tablePageProps"> v-bind="tablePageProps">
<template #highFormItem="formData"> <template #highFormItem="formData">
<ElFormItem label="业务类型">
<ElSelect v-model="formData.bizType" clearable placeholder="业务类型" @change="research">
<ElOption v-for="item in bizType" :key="item.val" :label="item.txt" :value="item.val"/>
</ElSelect>
</ElFormItem>
<ElFormItem label="编码"> <ElFormItem label="编码">
<ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/> <ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/>
</ElFormItem> </ElFormItem>
@ -19,11 +14,6 @@
</ElFormItem> </ElFormItem>
</template> </template>
<template #simpleFormItem="formData"> <template #simpleFormItem="formData">
<ElFormItem style="min-width: 200px">
<ElSelect v-model="formData.bizType" clearable placeholder="业务类型" @change="research">
<ElOption v-for="item in bizType" :key="item.val" :label="item.txt" :value="item.val"/>
</ElSelect>
</ElFormItem>
<ElFormItem> <ElFormItem>
<ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/> <ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/>
</ElFormItem> </ElFormItem>
@ -43,7 +33,7 @@
<ElTableColumn label="备注" prop="memo"/> <ElTableColumn label="备注" prop="memo"/>
<ElTableColumn label="创建时间" prop="createTime"/> <ElTableColumn label="创建时间" prop="createTime"/>
</template> </template>
<GoodsCategoryForm ref="goodsCategoryForm" :research="research"/> <GoodsCategoryForm ref="goodsCategoryForm" :default-biz-type="defaultBizType" :research="research"/>
</ATablePage> </ATablePage>
</template> </template>
@ -58,6 +48,10 @@ import ATablePage, {
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
const props = defineProps<{
defaultBizType: typeof bizType[number]['val']
}>()
const goodsCategoryFormIns = useTemplateRef<InstanceType<typeof GoodsCategoryForm>>('goodsCategoryForm') const goodsCategoryFormIns = useTemplateRef<InstanceType<typeof GoodsCategoryForm>>('goodsCategoryForm')
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
@ -69,6 +63,24 @@ const tablePageProps = buildTablePageProps<GoodsCategoryTypes.SearchGoodsCategor
pageLayout: { pageLayout: {
searchFormHeight: '120px', searchFormHeight: '120px',
dataListHeight: 1, dataListHeight: 1,
enableHighForm: false,
},
searchForm: {
defaultData: {createTimes: [ undefined, undefined ], bizType: props.defaultBizType},
highForm: {
contentWidth: 320,
},
simpleForm: {
colCount: 2,
},
paging(param) {
return GoodsCategoryApi.paging({
bizType: param.bizType,
categoryName: param.categoryName,
createTimeStart: param.createTimes?.[0],
createTimeEnd: param.createTimes?.[1],
})
},
}, },
toolBar: { toolBar: {
leftTools: [ leftTools: [
@ -110,19 +122,5 @@ const tablePageProps = buildTablePageProps<GoodsCategoryTypes.SearchGoodsCategor
], ],
}, },
}, },
searchForm: {
highForm: {
contentWidth: 320,
},
defaultData: {createTimes: [ undefined, undefined ]},
paging(param) {
return GoodsCategoryApi.paging({
bizType: param.bizType,
categoryName: param.categoryName,
createTimeStart: param.createTimes?.[0],
createTimeEnd: param.createTimes?.[1],
})
},
},
}) })
</script> </script>

View File

@ -1,6 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import ADropTable from '@/components/a-drop-table/ADropTable.vue' import ADropTable from '@/components/a-drop-table/ADropTable.vue'
import GoodsCategoryApi from '@/pages/gds/goods-category/goods-category-api.ts' import GoodsCategoryApi from '@/pages/gds/goods-category/goods-category-api.ts'
import { bizType } from '@/pages/gds/goods-category/constants.ts'
const props = defineProps<{
defaultBizType: typeof bizType[number]['val']
modelValue: string | undefined | null,
}>()
const dropTableColumns = [ const dropTableColumns = [
{ {
@ -13,13 +19,10 @@ const dropTableColumns = [
}, },
] ]
const dropTableLoader = (param: GoodsCategoryTypes.SearchGoodsCategoryParam) => { const dropTableLoader = (param: GoodsCategoryTypes.SearchGoodsCategoryParam) => {
param.bizType = props.defaultBizType
return GoodsCategoryApi.paging(param).then(res => res.data) return GoodsCategoryApi.paging(param).then(res => res.data)
} }
const props = defineProps<{
modelValue: string | undefined | null,
}>()
const emits = defineEmits<{ const emits = defineEmits<{
'update:modelValue': [ value: string | undefined | null ] 'update:modelValue': [ value: string | undefined | null ]
}>() }>()

View File

@ -8,7 +8,7 @@
<Uploader ref="uploader" v-model:file="formData.picture"/> <Uploader ref="uploader" v-model:file="formData.picture"/>
</ElFormItem> </ElFormItem>
<ElFormItem label="业务类型" prop="bizType"> <ElFormItem label="业务类型" prop="bizType">
<ElSelect v-model="formData.bizType" placeholder="业务类型"> <ElSelect v-model="formData.bizType" disabled placeholder="业务类型">
<ElOption v-for="item in bizType" :key="item.val" :label="item.txt" :value="item.val"/> <ElOption v-for="item in bizType" :key="item.val" :label="item.txt" :value="item.val"/>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
@ -41,6 +41,7 @@ import { bizType } from '@/pages/gds/goods-category/constants.ts'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
research?: () => void research?: () => void
defaultBizType: typeof bizType[number]['val']
}>(), { }>(), {
research: () => { research: () => {
}, },
@ -53,7 +54,9 @@ const formPanelProps = buildFormPanelProps<GoodsCategoryTypes.SearchGoodsCategor
detailsLoader(id?: string) { detailsLoader(id?: string) {
if (Strings.isBlank(id)) { if (Strings.isBlank(id)) {
status.value = 'add' status.value = 'add'
return Promise.resolve() return Promise.resolve({
bizType: props.defaultBizType,
} as GoodsCategoryTypes.SearchGoodsCategoryResult)
} else { } else {
status.value = 'modify' status.value = 'modify'
return GoodsCategoryApi.detail(id!) return GoodsCategoryApi.detail(id!)

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/gds/goods-category/GoodsCategory.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,8 @@
<script lang="ts" setup>
import GoodsCategory from '@/pages/gds/goods-category/GoodsCategory.vue'
</script>
<template>
<GoodsCategory default-biz-type="ZaiShengPin"/>
</template>

View File

@ -0,0 +1,5 @@
export default {
path: '/gds/product-category',
name: 'product-category',
component: () => import('@/pages/gds/goods-category/product/ProductCategory.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,8 @@
<script lang="ts" setup>
import GoodsCategory from '@/pages/gds/goods-category/GoodsCategory.vue'
</script>
<template>
<GoodsCategory default-biz-type="HuiShouPin"/>
</template>

View File

@ -0,0 +1,5 @@
export default {
path: '/gds/waste-category',
name: 'waste-category',
component: () => import('@/pages/gds/goods-category/waste/WasteCategory.vue'),
} as RouterTypes.RouteConfig

View File

@ -1,90 +0,0 @@
<script lang="ts" setup>
import {
expenseStrategy,
unit,
} from '@/pages/gds/goods/constants.ts'
import VModel from '@/common/utils/v-model.ts'
const props = defineProps<{
modelValue: {
//
expenseStrategy?: string
//
unit?: string
//
taxRate?: number
//
unitPrice?: number
//
initialPrice?: number
//
initialQuantity?: number
//
everyQuantity?: number
}
}>()
const emits = defineEmits([ 'update:modelValue' ])
const modelValueProxy = VModel<Pick<typeof props, 'modelValue'>>(props, 'modelValue', emits)
/*
watch(
() => props.modelValue.expenseStrategy,
(n) => {
switch (n) {
case expenseStrategy.DanJia:
rulesProxy.value?.unitPrice =
break
case expenseStrategy.GuDing:
break
case expenseStrategy.TanXing:
break
}
},
)
*/
</script>
<template>
<ElFormItem label="计费策略" prop="expenseStrategy">
<ElRadioGroup v-model="modelValueProxy.expenseStrategy">
<ElRadio v-for="it in expenseStrategy" :key="'expenseStrategy'+it.val" :value="it.val" border>{{ it.txt }}</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="计量单位" prop="unit">
<ElSelect v-model="modelValueProxy.unit">
<ElOption
v-for="item in unit"
:key="'unit'+item.val"
:label="item.txt"
:value="item.val"/>
</ElSelect>
</ElFormItem>
<ElFormItem label="税率" prop="taxRate">
<ElInputNumber v-model="modelValueProxy.taxRate" :max="100" :min="0" :precision="2" :step="0.01" controls-position="right"/>
</ElFormItem>
<ElFormItem v-if="modelValueProxy.expenseStrategy === expenseStrategy.TanXing" label="起步价" prop="initialPrice">
<ElInputNumber v-model="modelValueProxy.initialPrice" :min="0" :precision="2" :step="1" controls-position="right"/>
</ElFormItem>
<ElFormItem v-if="modelValueProxy.expenseStrategy === expenseStrategy.TanXing" label="起步量" prop="initialQuantity">
<ElInputNumber v-model="modelValueProxy.initialQuantity" :min="0" :step="1" controls-position="right"/>
</ElFormItem>
<ElFormItem
v-if="modelValueProxy.expenseStrategy !== expenseStrategy.MianFei"
:label="modelValueProxy.expenseStrategy === expenseStrategy.TanXing?'每档单价':(modelValueProxy.expenseStrategy === expenseStrategy.DanJia?'单价':'价格')"
prop="unitPrice">
<ElInputNumber v-model="modelValueProxy.unitPrice" :min="0" :precision="2" :step="1" controls-position="right"/>
</ElFormItem>
<ElFormItem v-if="modelValueProxy.expenseStrategy === expenseStrategy.TanXing" label="每档的量" prop="everyQuantity">
<ElInputNumber v-model="modelValueProxy.everyQuantity" :min="0" :step="1" controls-position="right"/>
</ElFormItem>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -4,7 +4,7 @@
v-bind="tablePageProps"> v-bind="tablePageProps">
<template #highFormItem="formData"> <template #highFormItem="formData">
<ElFormItem label="产品分类"> <ElFormItem label="产品分类">
<GoodsCategoryDropTable v-model="formData.goodsCategoryId"/> <GoodsCategoryDropTable v-model="formData.goodsCategoryId" :default-biz-type="defaultBizType"/>
</ElFormItem> </ElFormItem>
<ElFormItem label="产品编码"> <ElFormItem label="产品编码">
<ElInput v-model="formData.sn" placeholder="产品编码"/> <ElInput v-model="formData.sn" placeholder="产品编码"/>
@ -42,7 +42,7 @@
<ElTableColumn label="备注" prop="memo"/> <ElTableColumn label="备注" prop="memo"/>
<ElTableColumn label="创建时间" prop="createTime" width="160"/> <ElTableColumn label="创建时间" prop="createTime" width="160"/>
</template> </template>
<GoodsForm ref="goodsForm" :research="research"/> <GoodsForm ref="goodsForm" :default-biz-type="defaultBizType" :research="research"/>
</ATablePage> </ATablePage>
</template> </template>
@ -56,6 +56,11 @@ import ATablePage, {
type ATablePageInstance, type ATablePageInstance,
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
import { bizType } from '@/pages/gds/goods-category/constants.ts'
const props = defineProps<{
defaultBizType: typeof bizType[number]['val']
}>()
const goodsFormIns = useTemplateRef<InstanceType<typeof GoodsForm>>('goodsForm') const goodsFormIns = useTemplateRef<InstanceType<typeof GoodsForm>>('goodsForm')
@ -69,12 +74,16 @@ const tablePageProps = buildTablePageProps({
pageLayout: { pageLayout: {
searchFormHeight: '120px', searchFormHeight: '120px',
dataListHeight: 1, dataListHeight: 1,
enableHighForm: false,
}, },
searchForm: { searchForm: {
defaultData: {createTimes: [ undefined, undefined ], bizType: props.defaultBizType},
highForm: { highForm: {
contentWidth: 320, contentWidth: 320,
}, },
defaultData: {createTimes: [ undefined, undefined ]}, simpleForm: {
colCount: 2,
},
paging(param: GoodsTypes.SearchGoodsParam) { paging(param: GoodsTypes.SearchGoodsParam) {
return GoodsApi.paging({ return GoodsApi.paging({
...param, ...param,

View File

@ -13,7 +13,7 @@
<ElInput v-model="formData.sn" placeholder="产品编码" readonly/> <ElInput v-model="formData.sn" placeholder="产品编码" readonly/>
</ElFormItem> </ElFormItem>
<ElFormItem label="产品分类" prop="goodsCategoryId"> <ElFormItem label="产品分类" prop="goodsCategoryId">
<GoodsCategoryDropTable v-model="formData.goodsCategoryId"/> <GoodsCategoryDropTable v-model="formData.goodsCategoryId" :default-biz-type="defaultBizType"/>
</ElFormItem> </ElFormItem>
<ElFormItem label="产品名称" prop="goodsName"> <ElFormItem label="产品名称" prop="goodsName">
<ElInput v-model="formData.goodsName" placeholder="产品名称"/> <ElInput v-model="formData.goodsName" placeholder="产品名称"/>
@ -92,14 +92,15 @@ import {
expenseStrategy, expenseStrategy,
unit, unit,
} from '@/pages/gds/goods/constants.ts' } from '@/pages/gds/goods/constants.ts'
import { bizType } from '@/pages/gds/goods-category/constants.ts'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
research?: () => void research?: () => void
defaultBizType: typeof bizType[number]['val']
}>(), { }>(), {
research: () => { research: () => {
}, },
}) })
const formPanelIns = useTemplateRef<AFormPanelInstance>('formPanel') const formPanelIns = useTemplateRef<AFormPanelInstance>('formPanel')
const uploaderIns = useTemplateRef<InstanceType<typeof Uploader>>('uploader') const uploaderIns = useTemplateRef<InstanceType<typeof Uploader>>('uploader')
const status = ref<'add' | 'modify'>('add') const status = ref<'add' | 'modify'>('add')

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/gds/goods/Goods.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,7 @@
<script lang="ts" setup>
import Goods from '@/pages/gds/goods/Goods.vue'
</script>
<template>
<Goods default-biz-type="ZaiShengPin"/>
</template>

View File

@ -0,0 +1,5 @@
export default {
path: '/gds/product-goods',
name: 'product-goods',
component: () => import('@/pages/gds/goods/product/ProductGoods.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,7 @@
<script lang="ts" setup>
import Goods from '@/pages/gds/goods/Goods.vue'
</script>
<template>
<Goods default-biz-type="HuiShouPin"/>
</template>

View File

@ -0,0 +1,5 @@
export default {
path: '/gds/waste-goods',
name: 'waste-goods',
component: () => import('@/pages/gds/goods/waste/WasteGood.vue'),
} as RouterTypes.RouteConfig

View File

@ -16,7 +16,7 @@ const a = ref(false)
<h1>欢迎使用<br><span>再昇云智能收运系统</span></h1> <h1>欢迎使用<br><span>再昇云智能收运系统</span></h1>
<p>高效安全智能的一站式管理平台助力您的业务腾飞全新界面极致体验即刻开启您的数字化之旅</p> <p>高效安全智能的一站式管理平台助力您的业务腾飞全新界面极致体验即刻开启您的数字化之旅</p>
<!-- <a href="#" class="btn">进入系统</a> --> <!-- <a href="#" class="btn">进入系统</a> -->
<p> <!-- <p>
<ElButton :disabled="a" circle>Default</ElButton> <ElButton :disabled="a" circle>Default</ElButton>
<ElButton :disabled="a" type="primary">进入系统</ElButton> <ElButton :disabled="a" type="primary">进入系统</ElButton>
<ElButton :disabled="a" type="success">进入系统</ElButton> <ElButton :disabled="a" type="success">进入系统</ElButton>
@ -47,7 +47,7 @@ const a = ref(false)
<ElButton :disabled="a" link type="info">进入系统</ElButton> <ElButton :disabled="a" link type="info">进入系统</ElButton>
<ElButton :disabled="a" link type="warning">进入系统</ElButton> <ElButton :disabled="a" link type="warning">进入系统</ElButton>
<ElButton :disabled="a" link type="danger">进入系统</ElButton> <ElButton :disabled="a" link type="danger">进入系统</ElButton>
</p> </p> -->
</div> </div>
<!-- 右侧图形区 --> <!-- 右侧图形区 -->
@ -116,8 +116,8 @@ const a = ref(false)
color: #6C757D; color: #6C757D;
margin-bottom: 40px; margin-bottom: 40px;
line-height: 1.6; line-height: 1.6;
//max-width: 500px; max-width: 500px;
width: 7500px; //width: 7500px;
} }
/* 按钮样式 */ /* 按钮样式 */

View File

@ -79,22 +79,31 @@
<!-- <ElTableColumn label="客户备注" prop="customerMemo"/> --> <!-- <ElTableColumn label="客户备注" prop="customerMemo"/> -->
</template> </template>
<OrderForm ref="orderForm" :research="research"/> <BookForm ref="bookForm" :research="research"/>
<BookDetail ref="bookDetail"/> <BookDetail ref="bookDetail"/>
</ATablePage> </ATablePage>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import OrderForm from '@/pages/order/book/BookForm.vue' import BookForm from '@/pages/order/book/BookForm.vue'
import ADtPicker from '@/components/a-dt-picker/ADtPicker.vue' import ADtPicker from '@/components/a-dt-picker/ADtPicker.vue'
import ATablePage, { import ATablePage, {
type ATablePageInstance, type ATablePageInstance,
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
import { transStatus } from '@/pages/order/constants.ts' import {
orderCategory,
transStatus,
} from '@/pages/order/constants.ts'
import BookDetail from '@/pages/order/book/BookDetail.vue' import BookDetail from '@/pages/order/book/BookDetail.vue'
import type { R } from '@/common/utils/http-util.ts'
const props = defineProps<{
defaultOrderCategory: typeof orderCategory[number]['val']
}>()
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const bookFormIns = useTemplateRef<InstanceType<typeof BookForm>>('bookForm')
const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail') const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail')
function research() { function research() {
@ -105,32 +114,10 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
pageLayout: { pageLayout: {
dataListHeight: 3, dataListHeight: 3,
}, },
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '派单',
type: 'success',
show({row}) {
return row.transStatus === transStatus.DaiPaiDan
},
icon: 'Position',
action({row}) {
bookDetailIns.value?.open(row)
},
},
],
},
},
searchForm: { searchForm: {
defaultData: {
orderCategory: props.defaultOrderCategory,
},
highForm: { highForm: {
contentWidth: 342, contentWidth: 342,
}, },
@ -183,12 +170,47 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
transStatus: 'DaiPaiDan', transStatus: 'DaiPaiDan',
checkStatusTxt: '未勘料', checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付', paymentStatusTxt: '未支付',
}, } as OrderTypes.TableData,
], ],
}, },
}) } as R<G.PageResult<OrderTypes.TableData>>)
},
},
toolBar: {
leftTools: [
{
icon: 'Plus',
label: '新建',
action() {
bookFormIns.value?.open()
},
},
],
},
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '派单',
type: 'success',
show({row}) {
return row.transStatus === transStatus.DaiPaiDan
},
icon: 'Position',
action({row}) {
bookDetailIns.value?.open(row)
},
},
],
}, },
}, },
}) })
</script> </script>

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/order/book/Book.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Book from '@/pages/order/book/Book.vue'
</script>
<template>
<Book default-order-category="XiaoShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/product-book',
name: 'product-book',
component: () => import('@/pages/order/book/product/ProductBook.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Book from '@/pages/order/book/Book.vue'
</script>
<template>
<Book default-order-category="HuiShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/waste-book',
name: 'waste-book',
component: () => import('@/pages/order/book/waste/WasteBook.vue'),
} as RouterTypes.RouteConfig

View File

@ -92,11 +92,16 @@ import ATablePage, {
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
import BookDetail from '@/pages/order/book/BookDetail.vue' import BookDetail from '@/pages/order/book/BookDetail.vue'
import { orderCategory } from '@/pages/order/constants.ts'
import type { R } from '@/common/utils/http-util.ts'
const props = defineProps<{
defaultOrderCategory: typeof orderCategory[number]['val']
}>()
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail') const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail')
function research() { function research() {
tablePageIns.value?.doSearch() tablePageIns.value?.doSearch()
} }
@ -105,31 +110,10 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
pageLayout: { pageLayout: {
dataListHeight: 3, dataListHeight: 3,
}, },
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '删除',
type: 'danger',
icon: 'Delete',
confirm: {
title: '确认删除吗?',
},
action({row}) {
},
},
],
},
},
searchForm: { searchForm: {
defaultData: {
orderCategory: props.defaultOrderCategory,
},
highForm: { highForm: {
contentWidth: 342, contentWidth: 342,
}, },
@ -182,10 +166,35 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
transStatus: 'DaiPaiDan', transStatus: 'DaiPaiDan',
checkStatusTxt: '未勘料', checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付', paymentStatusTxt: '未支付',
}, } as OrderTypes.TableData,
], ],
}, },
}) } as R<G.PageResult<OrderTypes.TableData>>)
},
},
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '删除',
type: 'danger',
icon: 'Delete',
confirm: {
title: '确认删除吗?',
},
action() {
},
},
],
}, },
}, },
}) })

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/order/cancel/Cancel.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Cancel from '@/pages/order/cancel/Cancel.vue'
</script>
<template>
<Cancel default-order-category="XiaoShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/product-cancel',
name: 'product-cancel',
component: () => import('@/pages/order/cancel/product/ProductCancel.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Cancel from '@/pages/order/cancel/Cancel.vue'
</script>
<template>
<Cancel default-order-category="HuiShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/waste-cancel',
name: 'waste-cancel',
component: () => import('@/pages/order/cancel/waste/WasteCancel.vue'),
} as RouterTypes.RouteConfig

View File

@ -92,7 +92,12 @@ import ATablePage, {
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
import BookDetail from '@/pages/order/book/BookDetail.vue' import BookDetail from '@/pages/order/book/BookDetail.vue'
import { orderCategory } from '@/pages/order/constants.ts'
import type { R } from '@/common/utils/http-util.ts'
const props = defineProps<{
defaultOrderCategory: typeof orderCategory[number]['val']
}>()
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail') const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail')
@ -105,29 +110,10 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
pageLayout: { pageLayout: {
dataListHeight: 3, dataListHeight: 3,
}, },
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '三联单',
type: 'info',
icon: 'liandan',
action({row}) {
bookDetailIns.value?.open(row)
},
},
],
},
},
searchForm: { searchForm: {
defaultData: {
orderCategory: props.defaultOrderCategory,
},
highForm: { highForm: {
contentWidth: 342, contentWidth: 342,
}, },
@ -181,9 +167,39 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
checkStatusTxt: '未勘料', checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付', paymentStatusTxt: '未支付',
}, },
], ] as OrderTypes.TableData[],
}, },
}) } as R<G.PageResult<OrderTypes.TableData>>)
},
},
table: {
actionColumn: {
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '三联单',
type: 'info',
icon: 'liandan',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '历史轨迹',
type: 'info',
icon: 'liandan',
action({row}) {
bookDetailIns.value?.open(row)
},
},
],
}, },
}, },
}) })

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/order/history/History.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import History from '@/pages/order/history/History.vue'
</script>
<template>
<History default-order-category="XiaoShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/product-history',
name: 'product-history',
component: () => import('@/pages/order/history/product/ProductHistory.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import History from '@/pages/order/history/History.vue'
</script>
<template>
<History default-order-category="HuiShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/waste-history',
name: 'waste-history',
component: () => import('@/pages/order/history/waste/WasteHistory.vue'),
} as RouterTypes.RouteConfig

View File

@ -95,12 +95,17 @@ import ATablePage, {
import BookDetail from '@/pages/order/book/BookDetail.vue' import BookDetail from '@/pages/order/book/BookDetail.vue'
import { import {
checkStatus, checkStatus,
orderCategory,
transStatus, transStatus,
} from '@/pages/order/constants.ts' } from '@/pages/order/constants.ts'
import InOutPanel from '@/pages/order/realtime/InOutPanel.vue' import InOutPanel from '@/pages/order/realtime/InOutPanel.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useTemplateRef } from 'vue' import { useTemplateRef } from 'vue'
import type { R } from '@/common/utils/http-util.ts'
const props = defineProps<{
defaultOrderCategory: typeof orderCategory[number]['val']
}>()
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail') const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail')
const inOutPanelIns = useTemplateRef<InstanceType<typeof InOutPanel>>('inOutPanel') const inOutPanelIns = useTemplateRef<InstanceType<typeof InOutPanel>>('inOutPanel')
@ -113,58 +118,10 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
pageLayout: { pageLayout: {
dataListHeight: 3, dataListHeight: 3,
}, },
table: {
actionColumn: {
width: 100,
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '进场',
type: 'success',
icon: 'approach',
show({row}) {
return row.transStatus === transStatus.YunShuZhong
},
action({row}) {
inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '进场')
},
},
{
tooltip: '出场',
type: 'danger',
icon: 'carexit',
show({row}) {
return row.transStatus === transStatus.YiJinChang && (row.checkStatus === checkStatus.Wu || row.checkStatus === checkStatus.YiKanLiao)
},
action({row}) {
inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '出场')
},
},
{
tooltip: '勘料',
type: 'warning',
icon: 'chakanliaodan',
confirm: {
title: '是否确认勘料',
},
show({row}) {
return row.transStatus === transStatus.YiJinChang && row.checkStatus === checkStatus.WeiKanLiao
},
action({row}) {
ElMessage.success(`${row.truckLicensePlate!}勘料成功`)
},
},
],
},
},
searchForm: { searchForm: {
defaultData: {
orderCategory: props.defaultOrderCategory,
},
highForm: { highForm: {
contentWidth: 342, contentWidth: 342,
}, },
@ -266,9 +223,77 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
paymentStatusTxt: '未支付', paymentStatusTxt: '未支付',
truckLicensePlate: '苏A9B905', truckLicensePlate: '苏A9B905',
}, },
], ] as OrderTypes.TableData[],
}, },
}) } as R<G.PageResult<OrderTypes.TableData>>)
},
},
table: {
actionColumn: {
width: 190,
foldLimit: 5,
tableActions: [
{
tooltip: '详情',
icon: 'Postcard',
type: 'primary',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '三联单',
type: 'info',
icon: 'liandan',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '实时轨迹',
type: 'info',
icon: 'liandan',
action({row}) {
bookDetailIns.value?.open(row)
},
},
{
tooltip: '进场',
type: 'success',
icon: 'approach',
show({row}) {
return row.transStatus === transStatus.YunShuZhong
},
action({row}) {
inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '进场')
},
},
{
tooltip: '出场',
type: 'danger',
icon: 'carexit',
show({row}) {
return row.transStatus === transStatus.YiJinChang && (row.checkStatus === checkStatus.Wu || row.checkStatus === checkStatus.YiKanLiao)
},
action({row}) {
inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '出场')
},
},
{ // 退
tooltip: '勘料',
type: 'warning',
icon: 'chakanliaodan',
confirm: {
title: '是否确认勘料',
},
show({row}) {
return row.transStatus === transStatus.YiJinChang && row.checkStatus === checkStatus.WeiKanLiao
},
action({row}) {
ElMessage.success(`${row.truckLicensePlate!}勘料成功`)
},
},
],
}, },
}, },
}) })

View File

@ -1,3 +0,0 @@
export default {
component: () => import('@/pages/order/realtime/Realtime.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Realtime from '@/pages/order/realtime/Realtime.vue'
</script>
<template>
<Realtime default-order-category="XiaoShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/product-realtime',
name: 'product-realtime',
component: () => import('@/pages/order/realtime/product/ProductRealtime.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Realtime from '@/pages/order/realtime/Realtime.vue'
</script>
<template>
<Realtime default-order-category="HuiShouYuYue"/>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,5 @@
export default {
path: '/order/waste-realtime',
name: 'waste-realtime',
component: () => import('@/pages/order/realtime/waste/WasteRealtime.vue'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,464 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Element 风格颜色阶数计算器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", sans-serif;
}
body {
padding: 2rem;
background-color: #F5F7FA;
max-width: 1200px;
margin: 0 auto;
}
.container {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.input-group {
margin-bottom: 2rem;
}
.input-row {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.input-item {
flex: 1;
min-width: 250px;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #333333;
}
#baseColor, #varPrefix {
width: 100%;
padding: 0.8rem;
border: 1px solid #DCDFE6;
border-radius: 4px;
font-size: 1rem;
outline: none;
}
#baseColor:focus, #varPrefix:focus {
border-color: #5D87FF;
}
.options {
display: flex;
align-items: center;
gap: 1rem;
margin: 1rem 0;
}
.checkbox-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
button {
background-color: #5D87FF;
color: white;
border: none;
padding: 0.8rem 2rem;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
margin-right: 1rem;
}
button:hover {
background-color: #85A5FF;
}
.result-section {
margin-top: 2rem;
overflow-x: auto;
}
.color-row {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.color-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 1rem;
border-radius: 4px;
border: 1px solid #EEEEEE;
min-width: 120px;
cursor: pointer;
transition: all 0.2s;
}
.color-card:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.color-swatch {
width: 80px;
height: 80px;
border-radius: 4px;
margin-bottom: 0.8rem;
border: 1px solid #DDDDDD;
}
.color-info {
text-align: center;
}
.color-name {
font-weight: 600;
margin-bottom: 0.3rem;
color: #333333;
font-size: 0.9rem;
}
.color-code {
color: #666666;
font-family: monospace;
font-size: 0.9rem;
}
.css-code {
margin-top: 2rem;
padding: 1rem;
background-color: #F8F9FA;
border-radius: 4px;
font-family: monospace;
white-space: pre-wrap;
word-wrap: break-word;
color: #333333;
border: 1px solid #EEEEEE;
position: relative;
}
.copy-btn {
position: absolute;
top: 1rem;
right: 1rem;
padding: 0.4rem 0.8rem;
font-size: 0.8rem;
background-color: #4CAF50;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
}
.copy-btn:hover {
background-color: #45A049;
}
.tooltip {
position: fixed;
padding: 0.5rem 1rem;
background-color: rgba(0, 0, 0, 0.8);
color: white;
border-radius: 4px;
font-size: 0.9rem;
z-index: 9999;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
</style>
</head>
<body>
<div class="container">
<h1>Element 风格颜色阶数计算器</h1>
<div class="input-group">
<div class="input-row">
<div class="input-item">
<label for="baseColor">输入主色 HEX 值(如 #c93649、#FFCC55</label>
<input id="baseColor" placeholder="请输入带 # 的 HEX 色值,例如 #5D87FF" type="text" value="#D4A017">
</div>
<div class="input-item">
<label for="varPrefix">变量前缀(替代 primary</label>
<input id="varPrefix" placeholder="例如warning、danger、custom" type="text" value="warning">
</div>
</div>
<div class="options">
<div class="checkbox-item">
<input id="withHash" checked type="checkbox">
<label for="withHash">复制色值时包含 # 号</label>
</div>
</div>
<button onclick="calculateColorLevels()">计算色阶</button>
<button onclick="copyAllCss()">一键复制所有变量</button>
</div>
<div id="resultSection" class="result-section">
<!-- 色阶结果会动态生成 -->
</div>
<div id="cssCode" class="css-code">
<!-- CSS 代码会动态生成 -->
<button class="copy-btn" onclick="copyAllCss()">复制代码</button>
</div>
</div>
<!-- 复制提示弹窗 -->
<div id="tooltip" class="tooltip">复制成功!</div>
<script>
// 全局变量存储当前计算结果
let currentColorResults = {}
/**
* HEX 转 RGB
* @param {string} hex 带 # 的 HEX 色值
* @returns {object} r/g/b 数值0-255
*/
function hexToRgb(hex) {
hex = hex.replace(/^#/, '')
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('')
}
const r = parseInt(hex.substring(0, 2), 16)
const g = parseInt(hex.substring(2, 4), 16)
const b = parseInt(hex.substring(4, 6), 16)
return { r, g, b }
}
/**
* RGB 转 HEX
* @param {number} r 红0-255
* @param {number} g 绿0-255
* @param {number} b 蓝0-255
* @returns {string} 带 # 的 HEX 色值
*/
function rgbToHex(r, g, b) {
r = Math.max(0, Math.min(255, Math.round(r)))
g = Math.max(0, Math.min(255, Math.round(g)))
b = Math.max(0, Math.min(255, Math.round(b)))
return '#' +
r.toString(16).padStart(2, '0') +
g.toString(16).padStart(2, '0') +
b.toString(16).padStart(2, '0')
}
/**
* 计算颜色亮度(相对亮度)
* @param {number} r 红0-255
* @param {number} g 绿0-255
* @param {number} b 蓝0-255
* @returns {number} 亮度值0-100
*/
function getLuminance(r, g, b) {
const a = [ r, g, b ].map(v => {
v /= 255
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
})
return (a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722) * 100
}
/**
* 显示复制提示
* @param {string} text 提示文本
*/
function showTooltip(text) {
const tooltip = document.getElementById('tooltip')
tooltip.textContent = text
tooltip.style.opacity = 1
tooltip.style.left = (window.innerWidth / 2 - tooltip.offsetWidth / 2) + 'px'
tooltip.style.top = (window.innerHeight / 2) + 'px'
setTimeout(() => {
tooltip.style.opacity = 0
}, 1500)
}
/**
* 复制文本到剪贴板
* @param {string} text 要复制的文本
*/
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showTooltip('复制成功!')
}).catch(err => {
showTooltip('复制失败:' + err)
})
}
/**
* 核心:计算 Element 风格的颜色阶数
*/
function calculateColorLevels() {
const baseHex = document.getElementById('baseColor').value.trim()
const varPrefix = document.getElementById('varPrefix').value.trim() || 'primary'
// 验证 HEX 格式
if (!/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(baseHex)) {
alert('请输入有效的 HEX 色值,例如 #5D87FF 或 #fff')
return
}
const rgb = hexToRgb(baseHex)
const { r: r0, g: g0, b: b0 } = rgb
// 色阶调整参数
const levelConfig = {
'dark-2': { brightness: -0.18, saturation: 0.05 },
'light-3': { brightness: 0.10, saturation: -0.10 },
'light-5': { brightness: 0.20, saturation: -0.20 },
'light-7': { brightness: 0.30, saturation: -0.30 },
'light-8': { brightness: 0.35, saturation: -0.35 },
'light-9': { brightness: 0.40, saturation: -0.40 },
}
// 计算各阶颜色
currentColorResults = {
'主色': baseHex,
}
// 遍历计算每个阶数
Object.entries(levelConfig).forEach(([ level, config ]) => {
const newR = r0 + (255 - r0) * config.brightness
const newG = g0 + (255 - g0) * config.brightness
const newB = b0 + (255 - b0) * config.brightness
// 饱和度调整
const gray = (r0 + g0 + b0) / 3
const finalR = config.saturation > 0
? newR + (newR - gray) * config.saturation
: newR - (newR - gray) * Math.abs(config.saturation)
const finalG = config.saturation > 0
? newG + (newG - gray) * config.saturation
: newG - (newG - gray) * Math.abs(config.saturation)
const finalB = config.saturation > 0
? newB + (newB - gray) * config.saturation
: newB - (newB - gray) * Math.abs(config.saturation)
// 转 HEX 并保存
currentColorResults[level] = rgbToHex(finalR, finalG, finalB).toUpperCase()
})
// 渲染结果
renderResults(currentColorResults, varPrefix)
// 生成 CSS 代码
generateCssCode(currentColorResults, varPrefix)
}
/**
* 渲染颜色结果卡片(横向排列)
*/
function renderResults(results, varPrefix) {
const resultSection = document.getElementById('resultSection')
resultSection.innerHTML = ''
// 创建横向容器
const colorRow = document.createElement('div')
colorRow.className = 'color-row'
// 按固定顺序展示
const displayOrder = [ '主色', 'dark-2', 'light-3', 'light-5', 'light-7', 'light-8', 'light-9' ]
displayOrder.forEach(level => {
const hex = results[level]
const card = document.createElement('div')
card.className = 'color-card'
card.title = '点击复制色值'
// 颜色块
const swatch = document.createElement('div')
swatch.className = 'color-swatch'
swatch.style.backgroundColor = hex
// 颜色信息
const info = document.createElement('div')
info.className = 'color-info'
// 变量名
const varName = level === '主色'
? `--el-color-${varPrefix}`
: `--el-color-${varPrefix}-${level}`
info.innerHTML = `
<div class="color-name">${level}</div>
<div class="color-code" data-hex="${hex}">${hex}</div>
`
// 点击卡片复制色值
card.addEventListener('click', () => {
const withHash = document.getElementById('withHash').checked
const copyText = withHash ? hex : hex.replace(/^#/, '')
copyToClipboard(copyText)
})
card.appendChild(swatch)
card.appendChild(info)
colorRow.appendChild(card)
})
resultSection.appendChild(colorRow)
}
/**
* 生成 CSS 变量代码
*/
function generateCssCode(results, varPrefix) {
const cssCode = document.getElementById('cssCode')
const baseVar = `--el-color-${varPrefix}`
let css = `/* 基于 ${results['主色']} 的 Element 色阶配置 */\n`
css += `${baseVar}: ${results['主色']} !important;\n`
css += `${baseVar}-dark-2: ${results['dark-2']} !important;\n`
css += `${baseVar}-light-3: ${results['light-3']} !important;\n`
css += `${baseVar}-light-5: ${results['light-5']} !important;\n`
css += `${baseVar}-light-7: ${results['light-7']} !important;\n`
css += `${baseVar}-light-8: ${results['light-8']} !important;\n`
css += `${baseVar}-light-9: ${results['light-9']} !important;`
// 移除原有按钮,重新添加(避免重复)
cssCode.innerHTML = ''
const copyBtn = document.createElement('button')
copyBtn.className = 'copy-btn'
copyBtn.textContent = '复制代码'
copyBtn.onclick = copyAllCss
cssCode.appendChild(copyBtn)
cssCode.appendChild(document.createTextNode(css))
}
/**
* 一键复制所有 CSS 变量
*/
function copyAllCss() {
const cssCode = document.getElementById('cssCode')
// 排除按钮,只复制文本内容
const text = cssCode.textContent.replace('复制代码', '').trim()
copyToClipboard(text)
}
// 页面加载时默认计算一次
window.onload = calculateColorLevels
</script>
</body>
</html>