feat:添加设备分类页面/代码格式化

liutao_branch
黄伟杰 4 months ago
parent a22d95d134
commit b16865d0bf

@ -2,7 +2,7 @@ export interface RecipeConfigVO {
id: number id: number
recipeCode: string recipeCode: string
recipeName: string recipeName: string
recipeType?: string recipeType?: string | number
productId?: number productId?: number
productName?: string productName?: string
deviceId?: number deviceId?: number

@ -0,0 +1,58 @@
import request from '@/config/axios'
// 设备类型 VO
export interface DeviceTypeVO {
id: number // id
code: string // 编码
name: string // 名称
remark: string // 备注
sort: number // 排序
parentId?: number
parentChain?: string
createTime?: string
}
export interface DeviceTypeTreeVO extends DeviceTypeVO {
parentId?: number
parentChain?: string
createTime?: string
children?: DeviceTypeTreeVO[]
leaf?: boolean
}
// 设备类型 API
export const DeviceTypeApi = {
// 查询设备类型分页
getDeviceTypePage: async (params: any) => {
return await request.get({ url: `/mes/device-type/page`, params })
},
getDeviceTypeTree: async (params: any) => {
return await request.get({ url: `/mes/device-type/tree`, params })
},
// 查询设备类型详情
getDeviceType: async (id: number) => {
return await request.get({ url: `/mes/device-type/get?id=` + id })
},
// 新增设备类型
createDeviceType: async (data: DeviceTypeVO) => {
return await request.post({ url: `/mes/device-type/create`, data })
},
// 修改设备类型
updateDeviceType: async (data: DeviceTypeVO) => {
return await request.put({ url: `/mes/device-type/update`, data })
},
// 删除设备类型
deleteDeviceType: async (id: number) => {
return await request.delete({ url: `/mes/device-type/delete?id=` + id })
},
// 导出设备类型 Excel
exportDeviceType: async (params) => {
return await request.download({ url: `/mes/device-type/export-excel`, params })
}
}

@ -165,8 +165,8 @@ $toolbar-position: -55px;
width: 80px; width: 80px;
height: 25px; height: 25px;
font-size: 12px; font-size: 12px;
color: #6a6a6a;
line-height: 25px; line-height: 25px;
color: #6a6a6a;
text-align: center; text-align: center;
background: #fff; background: #fff;
box-shadow: box-shadow:

@ -94,9 +94,9 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
<style scoped lang="scss"> <style scoped lang="scss">
.editor-left { .editor-left {
z-index: 1; z-index: 1;
flex-shrink: 0;
user-select: none;
box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%); box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%);
user-select: none;
flex-shrink: 0;
:deep(.el-collapse) { :deep(.el-collapse) {
border-top: none; border-top: none;

@ -55,12 +55,12 @@ const handleToggleFab = () => {
/* 模态背景 */ /* 模态背景 */
.modal-bg { .modal-bg {
position: absolute; position: absolute;
left: calc(50% - 375px / 2);
top: 0; top: 0;
left: calc(50% - 375px / 2);
z-index: 11; z-index: 11;
width: 375px; width: 375px;
height: 100%; height: 100%;
background-color: rgba(#000000, 0.4); background-color: rgba(#000, 0.4);
} }
.fab-icon { .fab-icon {

@ -192,39 +192,39 @@ const handleAppLinkChange = (appLink: AppLink) => {
<style scoped lang="scss"> <style scoped lang="scss">
.hot-zone { .hot-zone {
position: absolute; position: absolute;
z-index: 10;
display: flex;
font-size: 16px;
color: var(--el-color-primary);
cursor: move;
background: var(--el-color-primary-light-7); background: var(--el-color-primary-light-7);
opacity: 0.8;
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
color: var(--el-color-primary); opacity: 0.8;
font-size: 16px;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: move;
z-index: 10;
/* 控制点 */ /* 控制点 */
.ctrl-dot { .ctrl-dot {
position: absolute; position: absolute;
z-index: 11;
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%;
border: inherit;
background-color: #fff; background-color: #fff;
z-index: 11; border: inherit;
border-radius: 50%;
} }
.delete { .delete {
display: none;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
display: none;
padding: 2px 2px 6px 6px; padding: 2px 2px 6px 6px;
background-color: var(--el-color-primary);
border-radius: 0 0 0 80%;
cursor: pointer;
color: #fff; color: #fff;
text-align: right; text-align: right;
cursor: pointer;
background-color: var(--el-color-primary);
border-radius: 0 0 0 80%;
} }
&:hover { &:hover {

@ -28,15 +28,15 @@ const props = defineProps<{ property: HotZoneProperty }>()
<style scoped lang="scss"> <style scoped lang="scss">
.hot-zone { .hot-zone {
position: absolute; position: absolute;
z-index: 10;
display: flex;
font-size: 14px;
color: var(--el-color-primary);
cursor: move;
background: var(--el-color-primary-light-7); background: var(--el-color-primary-light-7);
opacity: 0.8;
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
color: var(--el-color-primary); opacity: 0.8;
font-size: 14px;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: move;
z-index: 10;
} }
</style> </style>

@ -42,22 +42,22 @@ const handleOpenEditDialog = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.hot-zone { .hot-zone {
position: absolute; position: absolute;
display: flex;
font-size: 12px;
color: #fff;
cursor: move;
background: #409effbf; background: #409effbf;
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: move;
/* 控制点 */ /* 控制点 */
.ctrl-dot { .ctrl-dot {
position: absolute; position: absolute;
width: 4px; width: 4px;
height: 4px; height: 4px;
border-radius: 50%;
background-color: #fff; background-color: #fff;
border-radius: 50%;
} }
} }
</style> </style>

@ -103,13 +103,16 @@ watch(
.el-carousel__indicator { .el-carousel__indicator {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
.el-carousel__button { .el-carousel__button {
--el-carousel-indicator-height: 6px; --el-carousel-indicator-height: 6px;
--el-carousel-indicator-width: 6px; --el-carousel-indicator-width: 6px;
--el-carousel-indicator-out-color: #ff6000; --el-carousel-indicator-out-color: #ff6000;
border-radius: 6px; border-radius: 6px;
} }
} }
.el-carousel__indicator.is-active { .el-carousel__indicator.is-active {
.el-carousel__button { .el-carousel__button {
--el-carousel-indicator-width: 12px; --el-carousel-indicator-width: 12px;

@ -64,10 +64,10 @@ const getSearchProp = (cell: NavigationBarCellProperty) => {
.navigation-bar { .navigation-bar {
display: flex; display: flex;
height: 50px; height: 50px;
padding: 0 6px;
background: #fff; background: #fff;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0 6px;
/* 左边 */ /* 左边 */
.left { .left {

@ -545,11 +545,12 @@ $toolbar-height: 42px;
gap: 8px; gap: 8px;
:deep(.el-tag) { :deep(.el-tag) {
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
border: none; border: none;
box-shadow: 0 2px 8px 0 rgb(0 0 0 / 10%);
.el-tag__content { .el-tag__content {
width: 100%;
display: flex; display: flex;
width: 100%;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;

@ -50,6 +50,7 @@ watch(
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.el-input-group__append) { :deep(.el-input-group__append) {
padding: 0; padding: 0;
.el-color-picker__trigger { .el-color-picker__trigger {
padding: 0; padding: 0;
border-left: none; border-left: none;

@ -225,15 +225,16 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.cube-table { .cube-table {
position: relative; position: relative;
border-spacing: 0;
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0;
.cube { .cube {
border: 1px solid var(--el-border-color);
text-align: center;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);
text-align: center;
cursor: pointer; cursor: pointer;
border: 1px solid var(--el-border-color);
box-sizing: border-box; box-sizing: border-box;
&.active { &.active {
background: var(--el-color-primary-light-9); background: var(--el-color-primary-light-9);
} }
@ -242,28 +243,28 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
.hot-area { .hot-area {
position: absolute; position: absolute;
display: flex; display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--el-color-primary);
background: var(--el-color-primary-light-8);
color: var(--el-color-primary); color: var(--el-color-primary);
box-sizing: border-box;
border-spacing: 0;
border-collapse: collapse;
cursor: pointer; cursor: pointer;
background: var(--el-color-primary-light-8);
border: 1px solid var(--el-color-primary);
border-collapse: collapse;
border-spacing: 0;
box-sizing: border-box;
align-items: center;
justify-content: center;
.btn-delete { .btn-delete {
z-index: 1;
position: absolute; position: absolute;
top: -8px; top: -8px;
right: -8px; right: -8px;
height: 16px; z-index: 1;
width: 16px;
display: flex; display: flex;
width: 16px;
height: 16px;
background-color: #fff;
border-radius: 50%;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 50%;
background-color: #fff;
} }
} }
} }

@ -51,14 +51,14 @@ onMounted(async () => {
<style lang="scss"> <style lang="scss">
.markdown-view { .markdown-view {
font-family: PingFang SC; max-width: 100%;
font-family: "PingFang SC";
font-size: 0.95rem; font-size: 0.95rem;
font-weight: 400; font-weight: 400;
line-height: 1.6rem; line-height: 1.6rem;
letter-spacing: 0em; letter-spacing: 0;
text-align: left;
color: #3b3e55; color: #3b3e55;
max-width: 100%; text-align: left;
pre { pre {
position: relative; position: relative;
@ -69,22 +69,23 @@ onMounted(async () => {
} }
code.hljs { code.hljs {
border-radius: 6px;
padding-top: 20px;
width: auto; width: auto;
@media screen and (min-width: 1536px) { padding-top: 20px;
border-radius: 6px;
@media screen and (width >= 1536px) {
width: 960px; width: 960px;
} }
@media screen and (max-width: 1536px) and (min-width: 1024px) { @media screen and (width <= 1536px) and (width >= 1024px) {
width: calc(100vw - 400px - 64px - 32px * 2); width: calc(100vw - 400px - 64px - 32px * 2);
} }
@media screen and (max-width: 1024px) and (min-width: 768px) { @media screen and (width <= 1024px) and (width >= 768px) {
width: calc(100vw - 32px * 2); width: calc(100vw - 32px * 2);
} }
@media screen and (max-width: 768px) { @media screen and (width <= 768px) {
width: calc(100vw - 16px * 2); width: calc(100vw - 16px * 2);
} }
} }
@ -107,9 +108,9 @@ onMounted(async () => {
h4, h4,
h5, h5,
h6 { h6 {
color: var(--color-G900);
margin: 24px 0 8px; margin: 24px 0 8px;
font-weight: 600; font-weight: 600;
color: var(--el-text-color-primary);
} }
h1 { h1 {
@ -145,11 +146,11 @@ onMounted(async () => {
/* 列表(有序,无序) */ /* 列表(有序,无序) */
ul, ul,
ol { ol {
margin: 0 0 8px 0;
padding: 0; padding: 0;
margin: 0 0 8px;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
color: #3b3e55; // var(--color-CG600); color: #3b3e55; // var(--el-text-color-primary);
} }
li { li {
@ -158,8 +159,8 @@ onMounted(async () => {
} }
ol > li { ol > li {
list-style-type: decimal;
margin-bottom: 1rem; margin-bottom: 1rem;
list-style-type: decimal;
// , // ,
// &:nth-child(n + 10) { // &:nth-child(n + 10) {
// margin-left: 30px; // margin-left: 30px;
@ -171,23 +172,23 @@ onMounted(async () => {
} }
ul > li { ul > li {
list-style-type: disc;
font-size: 16px;
line-height: 24px;
margin-right: 11px; margin-right: 11px;
margin-bottom: 1rem; margin-bottom: 1rem;
font-size: 16px;
line-height: 24px;
color: #3b3e55; // var(--color-G900); color: #3b3e55; // var(--color-G900);
list-style-type: disc;
} }
ol ul, ol ul,
ol ul > li, ol ul > li,
ul ul, ul ul,
ul ul li { ul ul li {
margin-bottom: 1rem;
margin-left: 6px;
// list-style: circle; // list-style: circle;
font-size: 16px; font-size: 16px;
list-style: none; list-style: none;
margin-left: 6px;
margin-bottom: 1rem;
} }
ul ul ul, ul ul ul,

@ -139,11 +139,13 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
.el-transfer { .el-transfer {
display: flex; display: flex;
} }
.el-transfer__buttons { .el-transfer__buttons {
display: flex !important; display: flex !important;
flex-direction: column-reverse; flex-direction: column-reverse;
justify-content: center; justify-content: center;
gap: 20px; gap: 20px;
.el-transfer__button:nth-child(2) { .el-transfer__button:nth-child(2) {
margin: 0; margin: 0;
} }

@ -466,9 +466,9 @@ onMounted(async () => {
<style lang="scss" scoped> <style lang="scss" scoped>
.button-setting-pane { .button-setting-pane {
display: flex; display: flex;
flex-direction: column;
font-size: 14px;
margin-top: 8px; margin-top: 8px;
font-size: 14px;
flex-direction: column;
.button-setting-desc { .button-setting-desc {
padding-right: 8px; padding-right: 8px;

@ -296,7 +296,9 @@ const clear = () => {
$prefix-cls: #{$namespace}-setting; $prefix-cls: #{$namespace}-setting;
.#{$prefix-cls} { .#{$prefix-cls} {
z-index: 1200;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/
/* 修正没有z-index会被表格层覆盖,值不要超过4000 */
} }
</style> </style>

@ -91,7 +91,7 @@ const handleLock = async () => {
<style lang="scss" scoped> <style lang="scss" scoped>
:global(.v-lock-dialog) { :global(.v-lock-dialog) {
@media (max-width: 767px) { @media (width <= 767px) {
max-width: calc(100vw - 16px); max-width: calc(100vw - 16px);
} }
} }

@ -205,6 +205,7 @@ $error-color: #ed6f6f;
font-size: 90px; font-size: 90px;
} }
} }
@media screen and (min-width: $screen-lg) { @media screen and (min-width: $screen-lg) {
span:not(.meridiem) { span:not(.meridiem) {
font-size: 220px; font-size: 220px;
@ -216,6 +217,7 @@ $error-color: #ed6f6f;
font-size: 260px; font-size: 260px;
} }
} }
@media screen and (min-width: $screen-2xl) { @media screen and (min-width: $screen-2xl) {
span:not(.meridiem) { span:not(.meridiem) {
font-size: 320px; font-size: 320px;
@ -230,7 +232,7 @@ $error-color: #ed6f6f;
display: flex; display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgb(0 0 0 / 50%);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
justify-content: center; justify-content: center;
align-items: center; align-items: center;

@ -461,8 +461,8 @@ getAllApi()
</script> </script>
<style scoped> <style scoped>
.demo-progress .el-progress--line { .demo-progress .el-progress--line {
margin-bottom: 15px;
max-width: 600px; max-width: 600px;
margin-bottom: 15px;
} }
img{ img{

@ -205,15 +205,18 @@ onMounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
:deep() { :deep() {
.el-table--fit .el-table__inner-wrapper:before { .el-table--fit .el-table__inner-wrapper::before {
height: 0; height: 0;
} }
.el-card { .el-card {
border-radius: 8px; border-radius: 8px;
} }
.el-form--inline .el-form-item { .el-form--inline .el-form-item {
margin-right: 10px; margin-right: 10px;
} }
.el-divider--horizontal { .el-divider--horizontal {
margin-top: 6px; margin-top: 6px;
} }

@ -283,12 +283,13 @@ onMounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.process-definition-container::before { .process-definition-container::before {
content: '';
border-left: 1px solid #e6e6e6;
position: absolute; position: absolute;
left: 20.8%; left: 20.8%;
height: 100%; height: 100%;
border-left: 1px solid #e6e6e6;
content: '';
} }
:deep() { :deep() {
.definition-item-card { .definition-item-card {
.el-card__body { .el-card__body {

@ -42,8 +42,8 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box-card { .box-card {
height: 100%;
width: 100%; width: 100%;
height: 100%;
margin-bottom: 0; margin-bottom: 0;
:deep(.el-card__body) { :deep(.el-card__body) {
@ -52,9 +52,9 @@ watch(
} }
:deep(.process-viewer) { :deep(.process-viewer) {
width: 100%;
height: 100% !important; height: 100% !important;
min-height: 100%; min-height: 100%;
width: 100%;
overflow: auto; overflow: auto;
} }
} }

@ -155,13 +155,13 @@ const setSimpleModelNodeTaskStatus = (
<style lang="scss" scoped> <style lang="scss" scoped>
.process-viewer-container { .process-viewer-container {
height: 100%;
width: 100%; width: 100%;
height: 100%;
:deep(.process-viewer) { :deep(.process-viewer) {
width: 100%;
height: 100% !important; height: 100% !important;
min-height: 100%; min-height: 100%;
width: 100%;
overflow: auto; overflow: auto;
} }
} }

@ -1,42 +1,26 @@
<template> <template>
<ContentWrap> <ContentWrap>
<el-form <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="配方编码" prop="recipeCode"> <el-form-item label="配方编码" prop="recipeCode">
<el-input <el-input
v-model="queryParams.recipeCode" v-model="queryParams.recipeCode" placeholder="请输入配方编码" clearable @keyup.enter="handleQuery"
placeholder="请输入配方编码" class="!w-240px" />
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="配方名称" prop="recipeName"> <el-form-item label="配方名称" prop="recipeName">
<el-input <el-input
v-model="queryParams.recipeName" v-model="queryParams.recipeName" placeholder="请输入配方名称" clearable @keyup.enter="handleQuery"
placeholder="请输入配方名称" class="!w-240px" />
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="产品名称" prop="productName"> <el-form-item label="产品名称" prop="productName">
<el-input <el-input
v-model="queryParams.productName" v-model="queryParams.productName" placeholder="请输入产品名称" clearable @keyup.enter="handleQuery"
placeholder="请输入产品名称" class="!w-240px" />
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 查询</el-button> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 查询
</el-button>
<el-button type="primary" plain @click="openDialog('create')"> <el-button type="primary" plain @click="openDialog('create')">
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
@ -49,78 +33,60 @@
<ContentWrap> <ContentWrap>
<el-table <el-table
ref="tableRef" ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
v-loading="loading" highlight-current-row @selection-change="handleSelectionChange" @row-click="handleRowClick">
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="配方编码" align="center" prop="recipeCode" /> <el-table-column label="配方编码" align="center" prop="recipeCode" />
<el-table-column label="配方名称" align="center" prop="recipeName" /> <el-table-column label="配方名称" align="center" prop="recipeName" />
<el-table-column label="配方类型" align="center" prop="recipeType" /> <el-table-column label="配方类型" align="center" prop="recipeType">
<template #default="scope">
{{ getRecipeTypeLabel(scope.row.recipeType) }}
</template>
</el-table-column>
<el-table-column label="关联产品" align="center" prop="productName" /> <el-table-column label="关联产品" align="center" prop="productName" />
<el-table-column label="关联设备" align="center" prop="deviceName" /> <el-table-column label="关联设备" align="center" prop="deviceName" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" width="240px" fixed="right"> <el-table-column label="操作" align="center" width="240px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openConfigDialog(scope.row)"></el-button> <el-button link type="primary" @click.stop="openConfigDialog(scope.row)">配置</el-button>
<el-button link type="info" @click="openDetail(scope.row)"></el-button> <el-button link type="warning" @click.stop="openDialog('update', scope.row)">编辑</el-button>
<el-button link type="warning" @click="openDialog('update', scope.row)">编辑</el-button> <el-button link type="danger" @click.stop="handleDelete(scope.row)">删除</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<Pagination <Pagination
:total="total" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo" @pagination="handlePagination" />
v-model:limit="queryParams.pageSize"
@pagination="handlePagination"
/>
</ContentWrap> </ContentWrap>
<ContentWrap v-if="detailVisible"> <ContentWrap v-if="detailVisible">
<div class="flex items-center justify-between mb-12px"> <el-tabs v-model="detailActiveTab" class="mb-12px">
<div class="text-14px"> <template #extra>
详情{{ detailMeta.recipeCode }} - {{ detailMeta.recipeName }} <el-button link type="info" @click="closeDetail"></el-button>
</div> </template>
<el-button link type="info" @click="closeDetail"></el-button> <el-tab-pane :label="`详情:${detailMeta.recipeCode} - ${detailMeta.recipeName}`" name="detail">
</div> <el-table
<el-table v-loading="detailLoading" :data="detailList" :stripe="true" :show-overflow-tooltip="true"
v-loading="detailLoading" row-key="id">
:data="detailList" <el-table-column label="序号" align="center" width="80">
:stripe="true" <template #default="scope">
:show-overflow-tooltip="true" {{ (detailQueryParams.pageNo - 1) * detailQueryParams.pageSize + scope.$index + 1 }}
row-key="id" </template>
> </el-table-column>
<el-table-column label="序号" align="center" width="80"> <el-table-column label="点位名称" align="center" prop="pointName" />
<template #default="scope"> <el-table-column label="点位类型" align="center" prop="pointType" />
{{ (detailQueryParams.pageNo - 1) * detailQueryParams.pageSize + scope.$index + 1 }} <el-table-column label="数据类型" align="center" prop="dataType" />
</template> <el-table-column label="单位" align="center" prop="dataUnit" />
</el-table-column> </el-table>
<el-table-column label="点位名称" align="center" prop="pointName" /> <Pagination
<el-table-column label="点位类型" align="center" prop="pointType" /> :total="detailTotal" v-model:page="detailQueryParams.pageNo"
<el-table-column label="数据类型" align="center" prop="dataType" /> v-model:limit="detailQueryParams.pageSize" @pagination="handleDetailPagination" />
<el-table-column label="单位" align="center" prop="dataUnit" /> </el-tab-pane>
</el-table> </el-tabs>
<Pagination
:total="detailTotal"
v-model:page="detailQueryParams.pageNo"
v-model:limit="detailQueryParams.pageSize"
@pagination="handleDetailPagination"
/>
</ContentWrap> </ContentWrap>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="720px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="720px">
<el-form <el-form ref="dialogFormRef" :model="dialogForm" :rules="dialogRules" label-width="100px" v-loading="dialogLoading">
ref="dialogFormRef"
:model="dialogForm"
:rules="dialogRules"
label-width="100px"
v-loading="dialogLoading"
>
<el-form-item label="配方编码" prop="recipeCode"> <el-form-item label="配方编码" prop="recipeCode">
<el-input v-model="dialogForm.recipeCode" placeholder="请输入配方编码" clearable /> <el-input v-model="dialogForm.recipeCode" placeholder="请输入配方编码" clearable />
</el-form-item> </el-form-item>
@ -128,40 +94,24 @@
<el-input v-model="dialogForm.recipeName" placeholder="请输入配方名称" clearable /> <el-input v-model="dialogForm.recipeName" placeholder="请输入配方名称" clearable />
</el-form-item> </el-form-item>
<el-form-item label="配方类型" prop="recipeType"> <el-form-item label="配方类型" prop="recipeType">
<el-input v-model="dialogForm.recipeType" placeholder="请输入配方类型" clearable /> <el-select
v-model="dialogForm.recipeType" placeholder="请选择配方类型" clearable filterable class="!w-full"
:loading="recipeTypeLoading">
<el-option v-for="item in recipeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="关联产品" prop="productId"> <el-form-item label="关联产品" prop="productId">
<el-select <el-select
v-model="dialogForm.productId" v-model="dialogForm.productId" placeholder="请选择关联产品" clearable filterable class="!w-full"
placeholder="请选择关联产品" :loading="productLoading">
clearable <el-option v-for="item in productOptions" :key="item.value" :label="item.label" :value="item.value" />
filterable
class="!w-full"
:loading="productLoading"
>
<el-option
v-for="item in productOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关联设备" prop="deviceId"> <el-form-item label="关联设备" prop="deviceId">
<el-select <el-select
v-model="dialogForm.deviceId" v-model="dialogForm.deviceId" placeholder="请选择关联设备" clearable filterable class="!w-full"
placeholder="请选择关联设备" :loading="deviceLoading">
clearable <el-option v-for="item in deviceOptions" :key="item.value" :label="item.label" :value="item.value" />
filterable
class="!w-full"
:loading="deviceLoading"
>
<el-option
v-for="item in deviceOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
@ -177,11 +127,8 @@
<Dialog title="配置" v-model="configVisible" width="920px"> <Dialog title="配置" v-model="configVisible" width="920px">
<div v-loading="configLoading"> <div v-loading="configLoading">
<el-transfer <el-transfer
v-model="configSelectedKeys" class="formula-config-transfer" v-model="configSelectedKeys" :data="configCandidates" filterable
:data="configCandidates" :titles="['来源', '目标']" />
filterable
:titles="['候选点位', '已选点位']"
/>
</div> </div>
<template #footer> <template #footer>
<el-button @click="configVisible = false"> </el-button> <el-button @click="configVisible = false"> </el-button>
@ -194,6 +141,7 @@
import download from '@/utils/download' import download from '@/utils/download'
import { ProductApi } from '@/api/erp/product/product' import { ProductApi } from '@/api/erp/product/product'
import { DeviceApi } from '@/api/iot/device' import { DeviceApi } from '@/api/iot/device'
import { RecipeApi } from '@/api/iot/recipe'
import { RecipeConfigApi, RecipeConfigVO, RecipePointDetailVO } from '@/api/iot/recipeConfig' import { RecipeConfigApi, RecipeConfigVO, RecipePointDetailVO } from '@/api/iot/recipeConfig'
type SelectOption = { label: string; value: number } type SelectOption = { label: string; value: number }
@ -204,8 +152,6 @@ const message = useMessage()
const { t } = useI18n() const { t } = useI18n()
const loading = ref(false) const loading = ref(false)
const tableRef = ref()
const queryFormRef = ref()
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@ -278,13 +224,15 @@ const handleDelete = async (row: RecipeConfigVO) => {
await RecipeConfigApi.deleteRecipeConfig(row.id) await RecipeConfigApi.deleteRecipeConfig(row.id)
message.success(t('common.delSuccess')) message.success(t('common.delSuccess'))
await getList() await getList()
} catch {} } catch { }
} }
const productLoading = ref(false) const productLoading = ref(false)
const productOptions = ref<SelectOption[]>([]) const productOptions = ref<SelectOption[]>([])
const deviceLoading = ref(false) const deviceLoading = ref(false)
const deviceOptions = ref<SelectOption[]>([]) const deviceOptions = ref<SelectOption[]>([])
const recipeTypeLoading = ref(false)
const recipeTypeOptions = ref<SelectOption[]>([])
const productLabelMap = computed<Record<number, string>>(() => { const productLabelMap = computed<Record<number, string>>(() => {
return productOptions.value.reduce((acc, cur) => { return productOptions.value.reduce((acc, cur) => {
@ -300,6 +248,19 @@ const deviceLabelMap = computed<Record<number, string>>(() => {
}, {} as Record<number, string>) }, {} as Record<number, string>)
}) })
const recipeTypeLabelMap = computed<Record<number, string>>(() => {
return recipeTypeOptions.value.reduce((acc, cur) => {
acc[cur.value] = cur.label
return acc
}, {} as Record<number, string>)
})
const getRecipeTypeLabel = (value: unknown) => {
const num = Number(value)
if (!Number.isNaN(num) && recipeTypeLabelMap.value[num]) return recipeTypeLabelMap.value[num]
return (value as any) ?? ''
}
const getProductOptions = async () => { const getProductOptions = async () => {
productLoading.value = true productLoading.value = true
try { try {
@ -320,9 +281,23 @@ const getDeviceOptions = async () => {
} }
} }
const getRecipeTypeOptions = async () => {
recipeTypeLoading.value = true
try {
const data = await RecipeApi.getRecipePage(undefined)
recipeTypeOptions.value = (data?.list ?? []).map((item: any) => ({
label: item.name,
value: item.id
}))
} finally {
recipeTypeLoading.value = false
}
}
const ensureOptionsLoaded = async () => { const ensureOptionsLoaded = async () => {
if (!productOptions.value.length) await getProductOptions() if (!productOptions.value.length) await getProductOptions()
if (!deviceOptions.value.length) await getDeviceOptions() if (!deviceOptions.value.length) await getDeviceOptions()
if (!recipeTypeOptions.value.length) await getRecipeTypeOptions()
} }
type DialogMode = 'create' | 'update' type DialogMode = 'create' | 'update'
@ -335,7 +310,7 @@ const dialogForm = reactive({
id: undefined as number | undefined, id: undefined as number | undefined,
recipeCode: '', recipeCode: '',
recipeName: '', recipeName: '',
recipeType: '', recipeType: undefined as number | undefined,
productId: undefined as number | undefined, productId: undefined as number | undefined,
deviceId: undefined as number | undefined, deviceId: undefined as number | undefined,
remark: '' remark: ''
@ -358,7 +333,7 @@ const openDialog = async (mode: DialogMode, row?: RecipeConfigVO) => {
dialogForm.id = undefined dialogForm.id = undefined
dialogForm.recipeCode = '' dialogForm.recipeCode = ''
dialogForm.recipeName = '' dialogForm.recipeName = ''
dialogForm.recipeType = '' dialogForm.recipeType = undefined
dialogForm.productId = undefined dialogForm.productId = undefined
dialogForm.deviceId = undefined dialogForm.deviceId = undefined
dialogForm.remark = '' dialogForm.remark = ''
@ -368,7 +343,7 @@ const openDialog = async (mode: DialogMode, row?: RecipeConfigVO) => {
dialogForm.id = row?.id dialogForm.id = row?.id
dialogForm.recipeCode = row?.recipeCode ?? '' dialogForm.recipeCode = row?.recipeCode ?? ''
dialogForm.recipeName = row?.recipeName ?? '' dialogForm.recipeName = row?.recipeName ?? ''
dialogForm.recipeType = row?.recipeType ?? '' dialogForm.recipeType = typeof row?.recipeType === 'number' ? row.recipeType : Number(row?.recipeType) || undefined
dialogForm.productId = row?.productId dialogForm.productId = row?.productId
dialogForm.deviceId = row?.deviceId dialogForm.deviceId = row?.deviceId
dialogForm.remark = row?.remark ?? '' dialogForm.remark = row?.remark ?? ''
@ -405,6 +380,7 @@ const submitDialog = async () => {
} }
const detailVisible = ref(false) const detailVisible = ref(false)
const detailActiveTab = ref('detail')
const detailLoading = ref(false) const detailLoading = ref(false)
const detailList = ref<RecipePointDetailVO[]>([]) const detailList = ref<RecipePointDetailVO[]>([])
const detailTotal = ref(0) const detailTotal = ref(0)
@ -442,11 +418,19 @@ const openDetail = async (row: RecipeConfigVO) => {
detailQueryParams.recipeId = row.id detailQueryParams.recipeId = row.id
detailQueryParams.pageNo = 1 detailQueryParams.pageNo = 1
detailVisible.value = true detailVisible.value = true
detailActiveTab.value = 'detail'
await getDetailList() await getDetailList()
} }
const handleRowClick = async (row: RecipeConfigVO, column: any) => {
if (column?.type === 'selection') return
if (column?.label === '操作') return
await openDetail(row)
}
const closeDetail = () => { const closeDetail = () => {
detailVisible.value = false detailVisible.value = false
detailActiveTab.value = 'detail'
detailMeta.recipeId = undefined detailMeta.recipeId = undefined
detailMeta.recipeCode = '' detailMeta.recipeCode = ''
detailMeta.recipeName = '' detailMeta.recipeName = ''
@ -518,4 +502,21 @@ onMounted(() => {
}) })
</script> </script>
<style scoped></style> <style scoped>
:deep(.formula-config-transfer.el-transfer) {
--el-transfer-panel-body-height: 440px;
display: flex;
width: 100%;
}
:deep(.formula-config-transfer .el-transfer-panel) {
width: calc((100% - 96px) / 2);
}
:deep(.el-transfer__buttons) {
display: flex;
text-align: center;
align-items: center;
}
</style>

@ -3,7 +3,8 @@
<div class="single-device-dialog"> <div class="single-device-dialog">
<el-form class="-mb-15px" :inline="true" label-width="80px"> <el-form class="-mb-15px" :inline="true" label-width="80px">
<el-form-item label="采集时间"> <el-form-item label="采集时间">
<el-date-picker v-model="collectionTimeRange" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" <el-date-picker
v-model="collectionTimeRange" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange"
start-placeholder="开始时间" end-placeholder="结束时间" start-placeholder="开始时间" end-placeholder="结束时间"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" /> :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" />
</el-form-item> </el-form-item>
@ -19,15 +20,18 @@
采集时间{{ group.collectTime || '-' }} 采集时间{{ group.collectTime || '-' }}
</div> </div>
<div v-if="group.sections.length" class="single-device-dialog__table-grid"> <div v-if="group.sections.length" class="single-device-dialog__table-grid">
<div v-for="section in group.sections" :key="`${group.key}-${section.key}`" <div
v-for="section in group.sections" :key="`${group.key}-${section.key}`"
class="single-device-dialog__section"> class="single-device-dialog__section">
<div class="single-device-dialog__section-title"> <div class="single-device-dialog__section-title">
{{ section.title }} {{ section.title }}
</div> </div>
<el-empty v-if="!section.columns.length" description="暂无数据" /> <el-empty v-if="!section.columns.length" description="暂无数据" />
<el-table v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle" <el-table
v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle"
:cell-style="bodyCellStyle" size="small"> :cell-style="bodyCellStyle" size="small">
<el-table-column v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.label" <el-table-column
v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.label"
align="center"> align="center">
<template #default="scope"> <template #default="scope">
<span>{{ formatCell(scope.row[col.prop]) }}</span> <span>{{ formatCell(scope.row[col.prop]) }}</span>
@ -228,16 +232,16 @@ watch(
.single-device-dialog__record { .single-device-dialog__record {
padding: 12px; padding: 12px;
background: var(--el-bg-color-overlay);
border: 1px solid var(--el-border-color); border: 1px solid var(--el-border-color);
border-radius: 6px; border-radius: 6px;
background: var(--el-bg-color-overlay);
} }
.single-device-dialog__section { .single-device-dialog__section {
padding: 10px; padding: 10px;
background: var(--el-fill-color-lighter);
border: 1px solid var(--el-border-color); border: 1px solid var(--el-border-color);
border-radius: 6px; border-radius: 6px;
background: var(--el-fill-color-lighter);
} }
.single-device-dialog__section :deep(.el-table) { .single-device-dialog__section :deep(.el-table) {
@ -245,14 +249,14 @@ watch(
} }
.single-device-dialog__record-title { .single-device-dialog__record-title {
font-size: 13px;
margin-bottom: 6px; margin-bottom: 6px;
font-size: 13px;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);
} }
.single-device-dialog__section-title { .single-device-dialog__section-title {
font-size: 13px;
margin-bottom: 4px; margin-bottom: 4px;
font-size: 13px;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
} }

@ -204,8 +204,8 @@ watch(
} }
.single-device-dialog__section-title { .single-device-dialog__section-title {
font-size: 13px;
margin-bottom: 4px; margin-bottom: 4px;
font-size: 13px;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
} }

@ -136,11 +136,11 @@ const emitActivityChange = () => {
display: flex; display: flex;
width: 60px; width: 60px;
height: 60px; height: 60px;
cursor: pointer;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
border-radius: 8px; border-radius: 8px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer;
} }
.spu-pic { .spu-pic {

@ -208,8 +208,8 @@ onBeforeUnmount(() => {
//transition: border-left 0.05s ease-in-out; /* */ //transition: border-left 0.05s ease-in-out; /* */
.username { .username {
min-width: 0;
max-width: 60%; max-width: 60%;
min-width: 0;
} }
.last-message { .last-message {
@ -218,27 +218,27 @@ onBeforeUnmount(() => {
.last-message, .last-message,
.username { .username {
display: -webkit-box;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 1; -webkit-line-clamp: 1;
} }
} }
.active { .active {
background-color: rgba(128, 128, 128, 0.5); // background-color: rgb(128 128 128 / 50%); //
} }
.right-menu-ul { .right-menu-ul {
position: absolute; position: absolute;
background-color: var(--app-content-bg-color); width: 130px;
padding: 5px; padding: 5px;
margin: 0; margin: 0;
list-style-type: none; /* 移除默认的项目符号 */ list-style-type: none; /* 移除默认的项目符号 */
background-color: var(--app-content-bg-color);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */ box-shadow: 0 2px 4px rgb(0 0 0 / 10%); /* 阴影效果 */
width: 130px;
li { li {
padding: 8px 16px; padding: 8px 16px;

@ -372,36 +372,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.kefu { .kefu {
background-color: #f5f5f5;
position: relative; position: relative;
width: calc(100% - 300px - 260px); width: calc(100% - 300px - 260px);
background-color: #f5f5f5;
&::after { &::after {
content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 1px; /* 实际宽度 */ width: 1px; /* 实际宽度 */
height: 100%; height: 100%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
content: '';
transform: scaleX(0.3); /* 缩小宽度 */ transform: scaleX(0.3); /* 缩小宽度 */
} }
.kefu-header { .kefu-header {
background-color: #f5f5f5;
position: relative; position: relative;
display: flex; display: flex;
background-color: #f5f5f5;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
&::before { &::before {
content: '';
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 1px; /* 初始宽度 */ height: 1px; /* 初始宽度 */
background-color: var(--el-border-color); background-color: var(--el-border-color);
content: '';
transform: scaleY(0.3); /* 缩小视觉高度 */ transform: scaleY(0.3); /* 缩小视觉高度 */
} }
@ -412,30 +412,30 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
} }
&-content { &-content {
margin: 0;
padding: 10px;
position: relative; position: relative;
height: 100%;
width: 100%; width: 100%;
height: 100%;
padding: 10px;
margin: 0;
.newMessageTip { .newMessageTip {
position: absolute; position: absolute;
bottom: 35px;
right: 35px; right: 35px;
background-color: var(--app-content-bg-color); bottom: 35px;
padding: 10px; padding: 10px;
border-radius: 30px;
font-size: 12px; font-size: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */ background-color: var(--app-content-bg-color);
border-radius: 30px;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%); /* 阴影效果 */
} }
.ss-row-left { .ss-row-left {
justify-content: flex-start; justify-content: flex-start;
.kefu-message { .kefu-message {
background-color: #fff;
margin-left: 10px;
margin-top: 3px; margin-top: 3px;
margin-left: 10px;
background-color: #fff;
border-top-right-radius: 10px; border-top-right-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
@ -446,22 +446,22 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
justify-content: flex-end; justify-content: flex-end;
.kefu-message { .kefu-message {
background-color: rgb(206, 223, 255);
margin-right: 10px;
margin-top: 3px; margin-top: 3px;
border-top-left-radius: 10px; margin-right: 10px;
background-color: rgb(206 223 255);
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
border-top-left-radius: 10px;
} }
} }
// //
.kefu-message { .kefu-message {
color: #414141;
font-weight: 500;
padding: 5px 10px;
width: auto; width: auto;
max-width: 50%; max-width: 50%;
padding: 5px 10px;
font-weight: 500;
color: #414141;
//text-align: left; //text-align: left;
//display: inline-block !important; //display: inline-block !important;
//word-break: break-all; //word-break: break-all;
@ -475,30 +475,30 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
.date-message, .date-message,
.system-message { .system-message {
width: fit-content; width: fit-content;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 8px;
padding: 0 5px; padding: 0 5px;
color: #fff;
font-size: 10px; font-size: 10px;
color: #fff;
background-color: rgb(0 0 0 / 10%);
border-radius: 8px;
} }
} }
.kefu-footer { .kefu-footer {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column;
height: auto; height: auto;
margin: 0;
padding: 0; padding: 0;
margin: 0;
flex-direction: column;
&::before { &::before {
content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 1px; /* 初始宽度 */ height: 1px; /* 初始宽度 */
background-color: var(--el-border-color); background-color: var(--el-border-color);
content: '';
transform: scaleY(0.3); /* 缩小视觉高度 */ transform: scaleY(0.3); /* 缩小视觉高度 */
} }
@ -509,13 +509,13 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
} }
::v-deep(textarea) { ::v-deep(textarea) {
resize: none;
background-color: #f5f5f5; background-color: #f5f5f5;
resize: none;
} }
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
box-shadow: none !important;
border-radius: 0; border-radius: 0;
box-shadow: none !important;
} }
::v-deep(.el-textarea__inner) { ::v-deep(.el-textarea__inner) {

@ -170,31 +170,31 @@ const getUserData = async () => {
background-color: #f5f5f5; background-color: #f5f5f5;
&::after { &::after {
content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 1px; /* 实际宽度 */ width: 1px; /* 实际宽度 */
height: 100%; height: 100%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
content: '';
transform: scaleX(0.3); /* 缩小宽度 */ transform: scaleX(0.3); /* 缩小宽度 */
} }
&-header { &-header {
background-color: #f5f5f5;
position: relative; position: relative;
display: flex; display: flex;
background-color: #f5f5f5;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
&::before { &::before {
content: '';
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 1px; /* 初始宽度 */ height: 1px; /* 初始宽度 */
background-color: var(--el-border-color); background-color: var(--el-border-color);
content: '';
transform: scaleY(0.3); /* 缩小视觉高度 */ transform: scaleY(0.3); /* 缩小视觉高度 */
} }
@ -204,45 +204,39 @@ const getUserData = async () => {
} }
&-item { &-item {
height: 100%;
width: 100%;
position: relative; position: relative;
width: 100%;
height: 100%;
&-activation::before { &-activation::before {
content: '';
position: absolute; /* 绝对定位 */ position: absolute; /* 绝对定位 */
top: 0; inset: 0; /* 覆盖整个元素 */
left: 0;
right: 0;
bottom: 0; /* 覆盖整个元素 */
border-bottom: 2px solid rgba(128, 128, 128, 0.5); /* 边框样式 */
pointer-events: none; /* 确保点击事件不会被伪元素拦截 */ pointer-events: none; /* 确保点击事件不会被伪元素拦截 */
border-bottom: 2px solid rgb(128 128 128 / 50%); /* 边框样式 */
content: '';
} }
&:hover::before { &:hover::before {
content: '';
position: absolute; /* 绝对定位 */ position: absolute; /* 绝对定位 */
top: 0; inset: 0; /* 覆盖整个元素 */
left: 0;
right: 0;
bottom: 0; /* 覆盖整个元素 */
border-bottom: 2px solid rgba(128, 128, 128, 0.5); /* 边框样式 */
pointer-events: none; /* 确保点击事件不会被伪元素拦截 */ pointer-events: none; /* 确保点击事件不会被伪元素拦截 */
border-bottom: 2px solid rgb(128 128 128 / 50%); /* 边框样式 */
content: '';
} }
} }
} }
&-content { &-content {
margin: 0;
padding: 0;
position: relative; position: relative;
height: 100%;
width: 100%; width: 100%;
height: 100%;
padding: 0;
margin: 0;
} }
&-tabs { &-tabs {
height: 100%;
width: 100%; width: 100%;
height: 100%;
} }
} }

@ -109,10 +109,10 @@ function formatOrderStatus(order: any) {
<style lang="scss" scoped> <style lang="scss" scoped>
.order-list-card-box { .order-list-card-box {
border-radius: 10px;
padding: 10px; padding: 10px;
border: 1px var(--el-border-color) solid;
background-color: #fff; // background-color: #fff; //
border: 1px var(--el-border-color) solid;
border-radius: 10px;
.order-card-header { .order-card-header {
height: 28px; height: 28px;
@ -123,8 +123,8 @@ function formatOrderStatus(order: any) {
span { span {
&:hover { &:hover {
text-decoration: underline;
color: var(--left-menu-bg-active-color); color: var(--left-menu-bg-active-color);
text-decoration: underline;
} }
} }
} }
@ -144,9 +144,9 @@ function formatOrderStatus(order: any) {
} }
.discounts-money { .discounts-money {
font-family: OPPOSANS;
font-size: 16px; font-size: 16px;
line-height: normal; line-height: normal;
font-family: OPPOSANS;
} }
.pay-color { .pay-color {
@ -156,26 +156,26 @@ function formatOrderStatus(order: any) {
} }
.warning-color { .warning-color {
color: #faad14;
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
color: #faad14;
} }
.danger-color { .danger-color {
color: #ff3000;
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
color: #ff3000;
} }
.success-color { .success-color {
color: #52c41a;
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
color: #52c41a;
} }
.info-color { .info-color {
color: #999999;
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
color: #999;
} }
</style> </style>

@ -68,20 +68,20 @@ const openDetail = (spuId: number) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.button { .button {
background-color: #007bff;
color: white;
border: none;
padding: 5px 10px; padding: 5px 10px;
color: white;
cursor: pointer; cursor: pointer;
background-color: #007bff;
border: none;
} }
.product-warp { .product-warp {
display: flex;
width: 100%; width: 100%;
padding: 10px;
background-color: #fff; background-color: #fff;
border-radius: 8px; border-radius: 8px;
display: flex;
align-items: center; align-items: center;
padding: 10px;
&-left { &-left {
width: 70px; width: 70px;
@ -97,14 +97,14 @@ const openDetail = (spuId: number) => {
flex: 1; flex: 1;
.description { .description {
display: -webkit-box;
width: 100%; width: 100%;
overflow: hidden;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
display: -webkit-box;
-webkit-line-clamp: 1; /* 显示一行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1; /* 显示一行 */
} }
.price { .price {

@ -102,11 +102,11 @@ onBeforeUnmount(() => {
<style lang="scss"> <style lang="scss">
.kefu-layout { .kefu-layout {
position: absolute; position: absolute;
flex: 1;
top: 0; top: 0;
left: 0; left: 0;
height: 100%;
width: 100%; width: 100%;
height: 100%;
flex: 1;
} }
/* 定义滚动条样式 */ /* 定义滚动条样式 */
@ -117,15 +117,15 @@ onBeforeUnmount(() => {
/* 定义滚动条轨道 内阴影+圆角 */ /* 定义滚动条轨道 内阴影+圆角 */
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
box-shadow: inset 0 0 0 rgba(240, 240, 240, 0.5);
border-radius: 10px;
background-color: #fff; background-color: #fff;
border-radius: 10px;
box-shadow: inset 0 0 0 rgb(240 240 240 / 50%);
} }
/* 定义滑块 内阴影+圆角 */ /* 定义滑块 内阴影+圆角 */
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background-color: rgb(240 240 240 / 50%);
border-radius: 10px; border-radius: 10px;
box-shadow: inset 0 0 0 rgba(240, 240, 240, 0.5); box-shadow: inset 0 0 0 rgb(240 240 240 / 50%);
background-color: rgba(240, 240, 240, 0.5);
} }
</style> </style>

@ -132,11 +132,11 @@ const emitActivityChange = () => {
display: flex; display: flex;
width: 60px; width: 60px;
height: 60px; height: 60px;
cursor: pointer;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
border-radius: 8px; border-radius: 8px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer;
} }
.spu-pic { .spu-pic {

@ -134,11 +134,11 @@ const emitActivityChange = () => {
display: flex; display: flex;
width: 60px; width: 60px;
height: 60px; height: 60px;
cursor: pointer;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
border-radius: 8px; border-radius: 8px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer;
} }
.spu-pic { .spu-pic {

@ -394,11 +394,11 @@ onMounted(async () => {
.el-timeline-right-content { .el-timeline-right-content {
display: flex; display: flex;
align-items: center;
min-height: 30px; min-height: 30px;
padding: 10px; padding: 10px;
border-radius: var(--el-card-border-radius);
background-color: var(--app-content-bg-color); background-color: var(--app-content-bg-color);
border-radius: var(--el-card-border-radius);
align-items: center;
&::before { &::before {
position: absolute; position: absolute;

@ -70,8 +70,8 @@ withDefaults(defineProps<{ user: UserApi.UserVO; wallet: WalletApi.WalletVO; col
justify-content: space-between; justify-content: space-between;
.el-descriptions__label { .el-descriptions__label {
width: 120px;
display: block; display: block;
width: 120px;
text-align: left; text-align: left;
} }

@ -151,8 +151,8 @@ withDefaults(defineProps<{ user: UserApi.UserVO; mode?: string }>(), {
justify-content: space-between; justify-content: space-between;
.el-descriptions__label { .el-descriptions__label {
width: 120px;
display: block; display: block;
width: 120px;
text-align: left; text-align: left;
} }

@ -0,0 +1,117 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入编码" />
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" placeholder="请输入排序" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { DeviceTypeApi, DeviceTypeTreeVO, DeviceTypeVO } from '@/api/mes/devicetype'
/** 设备类型 表单 */
defineOptions({ name: 'DeviceTypeForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
code: undefined,
name: undefined,
remark: undefined,
sort: undefined,
parentId: undefined
})
const formRules = reactive({
code: [{ required: true, message: '编码不能为空', trigger: 'blur' }],
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number, parentRow?: DeviceTypeTreeVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
if (type === 'create') {
formData.value.parentId = parentRow?.id ?? 0
}
//
if (id) {
formLoading.value = true
try {
formData.value = await DeviceTypeApi.getDeviceType(id)
if (formData.value.parentId === undefined) {
formData.value.parentId = parentRow?.parentId ?? 0
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as DeviceTypeVO
if (formType.value === 'create') {
await DeviceTypeApi.createDeviceType(data)
message.success(t('common.createSuccess'))
} else {
await DeviceTypeApi.updateDeviceType(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
code: undefined,
name: undefined,
remark: undefined,
sort: undefined,
parentId: undefined
}
formRef.value?.resetFields()
}
</script>

@ -0,0 +1,233 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="编码" prop="code">
<el-input
v-model="queryParams.code"
placeholder="请输入编码"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['mes:device-type:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
default-expand-all
:tree-props="{ children: 'children' }"
>
<el-table-column label="id" align="center" prop="id" />
<el-table-column label="编码" align="center" prop="code" />
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="排序" align="center" prop="sort" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('create', undefined, scope.row)"
>
添加
</el-button>
<el-button
link
type="primary"
@click="openForm('update', scope.row.id, scope.row)"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-if="showPagination"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<DeviceTypeForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { DeviceTypeApi, DeviceTypeTreeVO } from '@/api/mes/devicetype'
import DeviceTypeForm from './DeviceTypeForm.vue'
/** 设备类型 列表 */
defineOptions({ name: 'DeviceType' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<DeviceTypeTreeVO[]>([]) //
const total = ref(0) //
const showPagination = ref(true)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
code: undefined,
name: undefined,
createTime: []
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const [createStartTime, createEndTime] = Array.isArray(queryParams.createTime)
? queryParams.createTime
: []
const data = await DeviceTypeApi.getDeviceTypeTree({
pageNo: queryParams.pageNo,
pageSize: queryParams.pageSize,
code: queryParams.code,
name: queryParams.name,
createStartTime: createStartTime || undefined,
createEndTime: createEndTime || undefined
})
if (Array.isArray(data)) {
list.value = data
total.value = data.length
showPagination.value = false
return
}
list.value = data.list
total.value = data.total
showPagination.value = true
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number, parentRow?: DeviceTypeTreeVO) => {
formRef.value.open(type, id, parentRow)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await DeviceTypeApi.deleteDeviceType(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await DeviceTypeApi.exportDeviceType(queryParams)
download.excel(data, '设备类型.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

@ -62,7 +62,7 @@
<el-divider /> <el-divider />
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border> <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="支付通道异步回调内容"> <el-descriptions-item label="支付通道异步回调内容">
<el-text style="white-space: pre-wrap; word-break: break-word"> <el-text style=" word-break: break-word;white-space: pre-wrap">
{{ detailData.extension.channelNotifyData }} {{ detailData.extension.channelNotifyData }}
</el-text> </el-text>
</el-descriptions-item> </el-descriptions-item>

@ -62,7 +62,7 @@
</el-descriptions> </el-descriptions>
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border> <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="支付通道异步回调内容"> <el-descriptions-item label="支付通道异步回调内容">
<el-text style="white-space: pre-wrap; word-break: break-word"> <el-text style=" word-break: break-word;white-space: pre-wrap">
{{ refundDetail.channelNotifyData }} {{ refundDetail.channelNotifyData }}
</el-text> </el-text>
</el-descriptions-item> </el-descriptions-item>

Loading…
Cancel
Save