Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts: # src/views/bpm/model/ModelForm.vue # src/views/bpm/model/index.vuepull/452/head
commit
fb19dd2476
|
@ -83,7 +83,8 @@
|
|||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"driver.js": "^1.3.1",
|
||||
"echarts": "^5.5.0",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "2.7.0",
|
||||
"element-plus": "2.8.0",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"jsencrypt": "^3.3.2",
|
||||
|
|
2560
pnpm-lock.yaml
2560
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -54,8 +54,4 @@ body {
|
|||
.#{$prefix-cls}-grey-mode {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.scrollbar__view {
|
||||
height: 99%!important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { getAccessToken } from '@/utils/auth'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { config } from '@/config/axios/config'
|
||||
import request from '@/config/axios'
|
||||
import request from '@/config/axios' // AI 思维导图 VO
|
||||
|
||||
// AI 思维导图 VO
|
||||
export interface MindMapVO {
|
||||
|
|
|
@ -24,20 +24,6 @@ export interface PropertyValueVO {
|
|||
remark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品属性值的明细
|
||||
*/
|
||||
export interface PropertyValueDetailVO {
|
||||
/** 属性项的编号 */
|
||||
propertyId: number // 属性的编号
|
||||
/** 属性的名称 */
|
||||
propertyName: string
|
||||
/** 属性值的编号 */
|
||||
valueId: number
|
||||
/** 属性值的名称 */
|
||||
valueName: string
|
||||
}
|
||||
|
||||
// ------------------------ 属性项 -------------------
|
||||
|
||||
// 创建属性项
|
||||
|
@ -65,6 +51,11 @@ export const getPropertyPage = (params: PageParam) => {
|
|||
return request.get({ url: '/product/property/page', params })
|
||||
}
|
||||
|
||||
// 获得属性项精简列表
|
||||
export const getPropertySimpleList = (): Promise<PropertyVO[]> => {
|
||||
return request.get({ url: '/product/property/simple-list' })
|
||||
}
|
||||
|
||||
// ------------------------ 属性值 -------------------
|
||||
|
||||
// 获得属性值分页
|
||||
|
@ -91,3 +82,8 @@ export const updatePropertyValue = (data: PropertyValueVO) => {
|
|||
export const deletePropertyValue = (id: number) => {
|
||||
return request.delete({ url: `/product/property/value/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得属性值精简列表
|
||||
export const getPropertyValueSimpleList = (propertyId: number): Promise<PropertyValueVO[]> => {
|
||||
return request.get({ url: '/product/property/value/simple-list', params: { propertyId } })
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ export interface Spu {
|
|||
giveIntegral?: number // 赠送积分
|
||||
virtualSalesCount?: number // 虚拟销量
|
||||
price?: number // 商品价格
|
||||
combinationPrice?: number // 商品拼团价格
|
||||
seckillPrice?: number // 商品秒杀价格
|
||||
salesCount?: number // 商品销量
|
||||
marketPrice?: number // 市场价
|
||||
costPrice?: number // 成本价
|
||||
|
|
|
@ -16,6 +16,7 @@ export interface CombinationActivityVO {
|
|||
virtualGroup?: number
|
||||
status?: number
|
||||
limitDuration?: number
|
||||
combinationPrice?: number
|
||||
products: CombinationProductVO[]
|
||||
}
|
||||
|
||||
|
@ -36,7 +37,7 @@ export interface SpuExtension extends Spu {
|
|||
}
|
||||
|
||||
// 查询拼团活动列表
|
||||
export const getCombinationActivityPage = async (params) => {
|
||||
export const getCombinationActivityPage = async (params: any) => {
|
||||
return await request.get({ url: '/promotion/combination-activity/page', params })
|
||||
}
|
||||
|
||||
|
@ -45,6 +46,11 @@ export const getCombinationActivity = async (id: number) => {
|
|||
return await request.get({ url: '/promotion/combination-activity/get?id=' + id })
|
||||
}
|
||||
|
||||
// 获得拼团活动列表,基于活动编号数组
|
||||
export const getCombinationActivityListByIds = (ids: number[]) => {
|
||||
return request.get({ url: `/promotion/combination-activity/list-by-ids?ids=${ids}` })
|
||||
}
|
||||
|
||||
// 新增拼团活动
|
||||
export const createCombinationActivity = async (data: CombinationActivityVO) => {
|
||||
return await request.post({ url: '/promotion/combination-activity/create', data })
|
||||
|
|
|
@ -74,7 +74,7 @@ export function getCouponTemplatePage(params: PageParam) {
|
|||
}
|
||||
|
||||
// 获得优惠劵模板分页
|
||||
export function getCouponTemplateList(ids: number[]) {
|
||||
export function getCouponTemplateList(ids: number[]): Promise<CouponTemplateVO[]> {
|
||||
return request.get({
|
||||
url: `/promotion/coupon-template/list?ids=${ids}`
|
||||
})
|
||||
|
|
|
@ -30,6 +30,6 @@ export const KeFuConversationApi = {
|
|||
},
|
||||
// 删除客服会话
|
||||
deleteConversation: async (id: number) => {
|
||||
return await request.get({ url: '/promotion/kefu-conversation/delete?id' + id })
|
||||
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,39 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface DiscountActivityVO {
|
||||
export interface RewardActivityVO {
|
||||
id?: number
|
||||
name?: string
|
||||
startTime?: Date
|
||||
endTime?: Date
|
||||
startAndEndTime?: Date[] // 只前端使用
|
||||
remark?: string
|
||||
conditionType?: number
|
||||
productScope?: number
|
||||
rules: RewardRule[]
|
||||
// 如下仅用于表单,不提交
|
||||
productScopeValues?: number[] // 商品范围:值为品类编号列表、商品编号列表
|
||||
productCategoryIds?: number[]
|
||||
productSpuIds?: number[]
|
||||
rules?: DiscountProductVO[]
|
||||
}
|
||||
|
||||
// 优惠规则
|
||||
export interface DiscountProductVO {
|
||||
limit: number
|
||||
discountPrice: number
|
||||
freeDelivery: boolean
|
||||
export interface RewardRule {
|
||||
limit?: number
|
||||
discountPrice?: number
|
||||
freeDelivery?: boolean
|
||||
point: number
|
||||
couponIds: number[]
|
||||
couponCounts: number[]
|
||||
giveCouponTemplateCounts?: {
|
||||
[key: number]: number
|
||||
}
|
||||
}
|
||||
|
||||
// 新增满减送活动
|
||||
export const createRewardActivity = async (data: DiscountActivityVO) => {
|
||||
export const createRewardActivity = async (data: RewardActivityVO) => {
|
||||
return await request.post({ url: '/promotion/reward-activity/create', data })
|
||||
}
|
||||
|
||||
// 更新满减送活动
|
||||
export const updateRewardActivity = async (data: DiscountActivityVO) => {
|
||||
export const updateRewardActivity = async (data: RewardActivityVO) => {
|
||||
return await request.put({ url: '/promotion/reward-activity/update', data })
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface SeckillActivityVO {
|
|||
// 秒杀活动所需属性
|
||||
export interface SeckillProductVO {
|
||||
skuId: number
|
||||
spuId: number
|
||||
seckillPrice: number
|
||||
stock: number
|
||||
}
|
||||
|
|
|
@ -46,8 +46,3 @@ export const updateUserLevel = async (data: any) => {
|
|||
export const updateUserPoint = async (data: any) => {
|
||||
return await request.put({ url: `/member/user/update-point`, data })
|
||||
}
|
||||
|
||||
// 修改会员用户余额
|
||||
export const updateUserBalance = async (data: any) => {
|
||||
return await request.put({ url: `/member/user/update-balance`, data })
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import request from '@/config/axios'
|
|||
|
||||
export interface AppVO {
|
||||
id: number
|
||||
appKey: string
|
||||
name: string
|
||||
status: number
|
||||
remark: string
|
||||
|
|
|
@ -4,6 +4,7 @@ import request from '@/config/axios'
|
|||
export interface PayWalletUserReqVO {
|
||||
userId: number
|
||||
}
|
||||
|
||||
/** 钱包 VO */
|
||||
export interface WalletVO {
|
||||
id: number
|
||||
|
@ -20,7 +21,12 @@ export const getWallet = async (params: PayWalletUserReqVO) => {
|
|||
return await request.get<WalletVO>({ url: `/pay/wallet/get`, params })
|
||||
}
|
||||
|
||||
// 查询会员钱包列表
|
||||
export const getWalletPage = async (params) => {
|
||||
/** 查询会员钱包列表 */
|
||||
export const getWalletPage = async (params: any) => {
|
||||
return await request.get({ url: `/pay/wallet/page`, params })
|
||||
}
|
||||
|
||||
/** 修改会员钱包余额 */
|
||||
export const updateWalletBalance = async (data: any) => {
|
||||
return await request.put({ url: `/pay/wallet/update-balance`, data })
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.4 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1724297262365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1396" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M707.91 103c16.28 0 29.522 13.007 29.897 29.195l0.009 0.706v111.878a29.96 29.96 0 0 1-0.898 7.3l171.177-0.001c16.28 0 29.522 13.007 29.897 29.195l0.008 0.706v637.12c0 16.278-13.01 29.518-29.2 29.893l-0.705 0.008H270.884c-16.28 0-29.522-13.007-29.897-29.195l-0.008-0.706V787.274c0-16.514 13.389-29.9 29.905-29.9 16.28 0 29.522 13.007 29.897 29.194l0.008 0.706v101.924h577.4V311.88h-577.4v88.787c0 16.278-13.009 29.518-29.2 29.893l-0.705 0.008c-16.28 0-29.522-13.008-29.897-29.195l-0.008-0.706V281.979c0-16.278 13.009-29.518 29.2-29.893l0.705-0.008h408.019a29.916 29.916 0 0 1-0.89-6.593l-0.008-0.706v-81.978H132.808v407.113h385.787L408.223 456.982c-11.36-11.624-11.329-30.143-0.066-41.729l0.554-0.555c11.625-11.358 30.147-11.327 41.734-0.066l0.555 0.554 161.028 164.762c11.244 11.504 11.344 29.793 0.362 41.42l-0.55 0.565-161.027 161.849c-11.648 11.707-30.583 11.757-42.292 0.11-11.524-11.461-11.754-29.979-0.657-41.723l0.546-0.563 111.319-111.89H102.905c-16.28 0-29.522-13.007-29.897-29.195l-0.008-0.705V132.9c0-16.278 13.01-29.518 29.2-29.893l0.705-0.008H707.91z" p-id="1397"></path></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -548,10 +548,10 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.second.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.second.type == '1'" label="范围">
|
||||
|
@ -607,10 +607,10 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.minute.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.minute.type == '1'" label="范围">
|
||||
|
@ -666,10 +666,10 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.hour.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.hour.type == '1'" label="范围">
|
||||
|
@ -725,12 +725,12 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.day.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button label="4">本月最后一天</el-radio-button>
|
||||
<el-radio-button label="5">不指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
<el-radio-button value="4">本月最后一天</el-radio-button>
|
||||
<el-radio-button value="5">不指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.day.type == '1'" label="范围">
|
||||
|
@ -786,10 +786,10 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.month.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.month.type == '1'" label="范围">
|
||||
|
@ -846,12 +846,12 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.week.type">
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button label="4">本月最后一周</el-radio-button>
|
||||
<el-radio-button label="5">不指定</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
<el-radio-button value="4">本月最后一周</el-radio-button>
|
||||
<el-radio-button value="5">不指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.week.type == '1'" label="范围">
|
||||
|
@ -925,11 +925,11 @@ const inputChange = () => {
|
|||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="cronValue.year.type">
|
||||
<el-radio-button label="-1">忽略</el-radio-button>
|
||||
<el-radio-button label="0">任意值</el-radio-button>
|
||||
<el-radio-button label="1">范围</el-radio-button>
|
||||
<el-radio-button label="2">间隔</el-radio-button>
|
||||
<el-radio-button label="3">指定</el-radio-button>
|
||||
<el-radio-button value="-1">忽略</el-radio-button>
|
||||
<el-radio-button value="0">任意值</el-radio-button>
|
||||
<el-radio-button value="1">范围</el-radio-button>
|
||||
<el-radio-button value="2">间隔</el-radio-button>
|
||||
<el-radio-button value="3">指定</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="cronValue.year.type == '1'" label="范围">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script lang="tsx">
|
||||
import { defineComponent, PropType, computed } from 'vue'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { isHexColor } from '@/utils/color'
|
||||
import { ElTag } from 'element-plus'
|
||||
import { DictDataType, getDictOptions } from '@/utils/dict'
|
||||
import { isArray, isString, isNumber } from '@/utils/is'
|
||||
import { isArray, isBoolean, isNumber, isString } from '@/utils/is'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DictTag',
|
||||
|
@ -29,15 +29,15 @@ export default defineComponent({
|
|||
},
|
||||
setup(props) {
|
||||
const valueArr: any = computed(() => {
|
||||
// 1.是Number类型的情况
|
||||
if (isNumber(props.value)) {
|
||||
// 1. 是 Number 类型和 Boolean 类型的情况
|
||||
if (isNumber(props.value) || isBoolean(props.value)) {
|
||||
return [String(props.value)]
|
||||
}
|
||||
// 2.是字符串(进一步判断是否有包含分隔符号 -> props.sepSymbol )
|
||||
// 2. 是字符串(进一步判断是否有包含分隔符号 -> props.sepSymbol )
|
||||
else if (isString(props.value)) {
|
||||
return props.value.split(props.separator)
|
||||
}
|
||||
// 3.数组
|
||||
// 3. 数组
|
||||
else if (isArray(props.value)) {
|
||||
return props.value.map(String)
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export default defineComponent({
|
|||
<div
|
||||
class="dict-tag"
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: 'inline-flex',
|
||||
gap: props.gutter,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
|
@ -72,7 +72,7 @@ export default defineComponent({
|
|||
// 添加标签的文字颜色为白色,解决自定义背景颜色时标签文字看不清的问题
|
||||
<ElTag
|
||||
style={dict?.cssClass ? 'color: #fff' : ''}
|
||||
type={dict?.colorType}
|
||||
type={dict?.colorType || null}
|
||||
color={dict?.cssClass && isHexColor(dict?.cssClass) ? dict?.cssClass : ''}
|
||||
disableTransitions={true}
|
||||
>
|
||||
|
|
|
@ -165,6 +165,7 @@ $toolbar-position: -55px;
|
|||
width: 80px;
|
||||
height: 25px;
|
||||
font-size: 12px;
|
||||
color: #6a6a6a;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
<el-form :model="formData" label-width="80px">
|
||||
<el-form-item label="组件背景" prop="bgType">
|
||||
<el-radio-group v-model="formData.bgType">
|
||||
<el-radio label="color">纯色</el-radio>
|
||||
<el-radio label="img">图片</el-radio>
|
||||
<el-radio value="color">纯色</el-radio>
|
||||
<el-radio value="img">图片</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
<el-form-item label="样式" prop="type">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-tooltip class="item" content="默认" placement="bottom">
|
||||
<el-radio-button label="default">
|
||||
<el-radio-button value="default">
|
||||
<Icon icon="system-uicons:carousel" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="卡片" placement="bottom">
|
||||
<el-radio-button label="card">
|
||||
<el-radio-button value="card">
|
||||
<Icon icon="ic:round-view-carousel" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
@ -18,8 +18,8 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="指示器" prop="indicator">
|
||||
<el-radio-group v-model="formData.indicator">
|
||||
<el-radio label="dot">小圆点</el-radio>
|
||||
<el-radio label="number">数字</el-radio>
|
||||
<el-radio value="dot">小圆点</el-radio>
|
||||
<el-radio value="number">数字</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否轮播" prop="autoplay">
|
||||
|
@ -43,8 +43,8 @@
|
|||
<template #default="{ element }">
|
||||
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
|
||||
<el-radio-group v-model="element.type">
|
||||
<el-radio label="img">图片</el-radio>
|
||||
<el-radio label="video">视频</el-radio>
|
||||
<el-radio value="img">图片</el-radio>
|
||||
<el-radio value="video">视频</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
|
|
|
@ -26,17 +26,17 @@
|
|||
<el-form-item label="列数" prop="type">
|
||||
<el-radio-group v-model="formData.columns">
|
||||
<el-tooltip class="item" content="一列" placement="bottom">
|
||||
<el-radio-button :label="1">
|
||||
<el-radio-button :value="1">
|
||||
<Icon icon="fluent:text-column-one-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="二列" placement="bottom">
|
||||
<el-radio-button :label="2">
|
||||
<el-radio-button :value="2">
|
||||
<Icon icon="fluent:text-column-two-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button :label="3">
|
||||
<el-radio-button :value="3">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
:key="index"
|
||||
:content="item.text"
|
||||
>
|
||||
<el-radio-button :label="item.type">
|
||||
<el-radio-button :value="item.type">
|
||||
<Icon :icon="item.icon" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
@ -24,12 +24,12 @@
|
|||
<el-form-item label="左右边距" prop="paddingType">
|
||||
<el-radio-group v-model="formData!.paddingType">
|
||||
<el-tooltip content="无边距" placement="top">
|
||||
<el-radio-button label="none">
|
||||
<el-radio-button value="none">
|
||||
<Icon icon="tabler:box-padding" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="左右留边" placement="top">
|
||||
<el-radio-button label="horizontal">
|
||||
<el-radio-button value="horizontal">
|
||||
<Icon icon="vaadin:padding" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<el-card header="按钮配置" class="property-group" shadow="never">
|
||||
<el-form-item label="展开方向" prop="direction">
|
||||
<el-radio-group v-model="formData.direction">
|
||||
<el-radio label="vertical">垂直</el-radio>
|
||||
<el-radio label="horizontal">水平</el-radio>
|
||||
<el-radio value="vertical">垂直</el-radio>
|
||||
<el-radio value="horizontal">水平</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="显示文字" prop="showText">
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<el-form-item label="每行数量" prop="column">
|
||||
<el-radio-group v-model="formData.column">
|
||||
<el-radio :label="3">3个</el-radio>
|
||||
<el-radio :label="4">4个</el-radio>
|
||||
<el-radio :value="3">3个</el-radio>
|
||||
<el-radio :value="4">4个</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<el-form-item label="布局" prop="layout">
|
||||
<el-radio-group v-model="formData.layout">
|
||||
<el-radio label="iconText">图标+文字</el-radio>
|
||||
<el-radio label="icon">仅图标</el-radio>
|
||||
<el-radio value="iconText">图标+文字</el-radio>
|
||||
<el-radio value="icon">仅图标</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="行数" prop="row">
|
||||
<el-radio-group v-model="formData.row">
|
||||
<el-radio :label="1">1行</el-radio>
|
||||
<el-radio :label="2">2行</el-radio>
|
||||
<el-radio :value="1">1行</el-radio>
|
||||
<el-radio :value="2">2行</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="列数" prop="column">
|
||||
<el-radio-group v-model="formData.column">
|
||||
<el-radio :label="3">3列</el-radio>
|
||||
<el-radio :label="4">4列</el-radio>
|
||||
<el-radio :label="5">5列</el-radio>
|
||||
<el-radio :value="3">3列</el-radio>
|
||||
<el-radio :value="4">4列</el-radio>
|
||||
<el-radio :value="5">5列</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
<template v-if="selectedHotAreaIndex === cellIndex">
|
||||
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
|
||||
<el-radio-group v-model="cell.type">
|
||||
<el-radio label="text">文字</el-radio>
|
||||
<el-radio label="image">图片</el-radio>
|
||||
<el-radio label="search">搜索框</el-radio>
|
||||
<el-radio value="text">文字</el-radio>
|
||||
<el-radio value="image">图片</el-radio>
|
||||
<el-radio value="search">搜索框</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 1. 文字 -->
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
<el-form label-width="80px" :model="formData" :rules="rules">
|
||||
<el-form-item label="样式" prop="styleType">
|
||||
<el-radio-group v-model="formData!.styleType">
|
||||
<el-radio label="normal">标准</el-radio>
|
||||
<el-radio value="normal">标准</el-radio>
|
||||
<el-tooltip
|
||||
content="沉侵式头部仅支持微信小程序、APP,建议页面第一个组件为图片展示类组件"
|
||||
placement="top"
|
||||
>
|
||||
<el-radio label="inner">沉浸式</el-radio>
|
||||
<el-radio value="inner">沉浸式</el-radio>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'">
|
||||
<el-radio-group v-model="formData!.alwaysShow">
|
||||
<el-radio :label="false">关闭</el-radio>
|
||||
<el-radio :value="false">关闭</el-radio>
|
||||
<el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
|
||||
<el-radio :label="true">开启</el-radio>
|
||||
<el-radio :value="true">开启</el-radio>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="背景类型" prop="bgType">
|
||||
<el-radio-group v-model="formData.bgType">
|
||||
<el-radio label="color">纯色</el-radio>
|
||||
<el-radio label="img">图片</el-radio>
|
||||
<el-radio value="color">纯色</el-radio>
|
||||
<el-radio value="img">图片</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
<el-form-item label="显示次数" :prop="`list[${index}].showType`">
|
||||
<el-radio-group v-model="element.showType">
|
||||
<el-tooltip content="只显示一次,下次打开时不显示" placement="bottom">
|
||||
<el-radio label="once">一次</el-radio>
|
||||
<el-radio value="once">一次</el-radio>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="每次打开时都会显示" placement="bottom">
|
||||
<el-radio label="always">不限</el-radio>
|
||||
<el-radio value="always">不限</el-radio>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
|
|
@ -67,15 +67,15 @@
|
|||
class="text-16px"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
>
|
||||
¥{{ spu.price }}
|
||||
¥{{ fenToYuan(spu.price) }}
|
||||
</span>
|
||||
<!-- 市场价 -->
|
||||
<span
|
||||
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||
class="ml-4px text-10px line-through"
|
||||
:style="{ color: property.fields.marketPrice.color }"
|
||||
>¥{{ spu.marketPrice }}</span
|
||||
>
|
||||
>¥{{ fenToYuan(spu.marketPrice) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-12px">
|
||||
<!-- 销量 -->
|
||||
|
@ -117,6 +117,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ProductCardProperty } from './config'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import { fenToYuan } from '../../../../../utils'
|
||||
|
||||
/** 商品卡片 */
|
||||
defineOptions({ name: 'ProductCard' })
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
<el-form-item label="布局" prop="type">
|
||||
<el-radio-group v-model="formData.layoutType">
|
||||
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||
<el-radio-button label="oneColBigImg">
|
||||
<el-radio-button value="oneColBigImg">
|
||||
<Icon icon="fluent:text-column-one-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||
<el-radio-button label="oneColSmallImg">
|
||||
<el-radio-button value="oneColSmallImg">
|
||||
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="双列" placement="bottom">
|
||||
<el-radio-button label="twoCol">
|
||||
<el-radio-button value="twoCol">
|
||||
<Icon icon="fluent:text-column-two-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
@ -74,8 +74,8 @@
|
|||
<el-card header="按钮" class="property-group" shadow="never">
|
||||
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||
<el-radio-group v-model="formData.btnBuy.type">
|
||||
<el-radio-button label="text">文字</el-radio-button>
|
||||
<el-radio-button label="img">图片</el-radio-button>
|
||||
<el-radio-button value="text">文字</el-radio-button>
|
||||
<el-radio-button value="img">图片</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<template v-if="formData.btnBuy.type === 'text'">
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
class="text-12px"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
>
|
||||
¥{{ spu.price }}
|
||||
¥{{ fenToYuan(spu.price) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,6 +65,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ProductListProperty } from './config'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import { fenToYuan } from './index'
|
||||
|
||||
/** 商品栏 */
|
||||
defineOptions({ name: 'ProductList' })
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
<el-form-item label="布局" prop="type">
|
||||
<el-radio-group v-model="formData.layoutType">
|
||||
<el-tooltip class="item" content="双列" placement="bottom">
|
||||
<el-radio-button label="twoCol">
|
||||
<el-radio-button value="twoCol">
|
||||
<Icon icon="fluent:text-column-two-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button label="threeCol">
|
||||
<el-radio-button value="threeCol">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="水平滑动" placement="bottom">
|
||||
<el-radio-button label="horizSwiper">
|
||||
<el-radio-button value="horizSwiper">
|
||||
<Icon icon="system-uicons:carousel" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
|
|
@ -3,13 +3,21 @@ import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
|||
/** 拼团属性 */
|
||||
export interface PromotionCombinationProperty {
|
||||
// 布局类型:单列 | 三列
|
||||
layoutType: 'oneCol' | 'threeCol'
|
||||
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'
|
||||
// 商品字段
|
||||
fields: {
|
||||
// 商品名称
|
||||
name: PromotionCombinationFieldProperty
|
||||
// 商品简介
|
||||
introduction: PromotionCombinationFieldProperty
|
||||
// 商品价格
|
||||
price: PromotionCombinationFieldProperty
|
||||
// 市场价
|
||||
marketPrice: PromotionCombinationFieldProperty
|
||||
// 商品销量
|
||||
salesCount: PromotionCombinationFieldProperty
|
||||
// 商品库存
|
||||
stock: PromotionCombinationFieldProperty
|
||||
}
|
||||
// 角标
|
||||
badge: {
|
||||
|
@ -18,6 +26,19 @@ export interface PromotionCombinationProperty {
|
|||
// 角标图片
|
||||
imgUrl: string
|
||||
}
|
||||
// 按钮
|
||||
btnBuy: {
|
||||
// 类型:文字 | 图片
|
||||
type: 'text' | 'img'
|
||||
// 文字
|
||||
text: string
|
||||
// 文字按钮:背景渐变起始颜色
|
||||
bgBeginColor: string
|
||||
// 文字按钮:背景渐变结束颜色
|
||||
bgEndColor: string
|
||||
// 图片按钮:图片地址
|
||||
imgUrl: string
|
||||
}
|
||||
// 上圆角
|
||||
borderRadiusTop: number
|
||||
// 下圆角
|
||||
|
@ -25,7 +46,7 @@ export interface PromotionCombinationProperty {
|
|||
// 间距
|
||||
space: number
|
||||
// 拼团活动编号
|
||||
activityId: number
|
||||
activityIds: number[]
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
@ -44,12 +65,23 @@ export const component = {
|
|||
name: '拼团',
|
||||
icon: 'mdi:account-group',
|
||||
property: {
|
||||
layoutType: 'oneCol',
|
||||
layoutType: 'oneColBigImg',
|
||||
fields: {
|
||||
name: { show: true, color: '#000' },
|
||||
price: { show: true, color: '#ff3000' }
|
||||
introduction: { show: true, color: '#999' },
|
||||
price: { show: true, color: '#ff3000' },
|
||||
marketPrice: { show: true, color: '#c4c4c4' },
|
||||
salesCount: { show: true, color: '#c4c4c4' },
|
||||
stock: { show: false, color: '#c4c4c4' }
|
||||
},
|
||||
badge: { show: false, imgUrl: '' },
|
||||
btnBuy: {
|
||||
type: 'text',
|
||||
text: '去拼团',
|
||||
bgBeginColor: '#FF6000',
|
||||
bgEndColor: '#FE832A',
|
||||
imgUrl: ''
|
||||
},
|
||||
borderRadiusTop: 8,
|
||||
borderRadiusBottom: 8,
|
||||
space: 8,
|
||||
|
|
|
@ -1,125 +1,201 @@
|
|||
<template>
|
||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
||||
<!-- 商品网格 -->
|
||||
<div :class="`box-content min-h-30px w-full flex flex-row flex-wrap`" ref="containerRef">
|
||||
<div
|
||||
class="grid overflow-x-auto"
|
||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
:style="{
|
||||
gridGap: `${property.space}px`,
|
||||
gridTemplateColumns,
|
||||
width: scrollbarWidth
|
||||
...calculateSpace(index),
|
||||
...calculateWidth(),
|
||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||
}"
|
||||
v-for="(spu, index) in spuList"
|
||||
:key="index"
|
||||
>
|
||||
<!-- 商品 -->
|
||||
<!-- 角标 -->
|
||||
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||
</div>
|
||||
<!-- 商品封面图 -->
|
||||
<div
|
||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
:style="{
|
||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||
}"
|
||||
v-for="(spu, index) in spuList"
|
||||
:key="index"
|
||||
:class="[
|
||||
'h-140px',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<!-- 角标 -->
|
||||
<div
|
||||
v-if="property.badge.show"
|
||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||
>
|
||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||
</div>
|
||||
<!-- 商品封面图 -->
|
||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
||||
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
' flex flex-col gap-8px p-8px box-border',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<!-- 商品名称 -->
|
||||
<div
|
||||
v-if="property.fields.name.show"
|
||||
:class="[
|
||||
'flex flex-col gap-8px p-8px box-border',
|
||||
'text-14px ',
|
||||
{
|
||||
'w-[calc(100%-64px)]': columns === 2,
|
||||
'w-full': columns === 3
|
||||
truncate: property.layoutType !== 'oneColSmallImg',
|
||||
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
:style="{ color: property.fields.name.color }"
|
||||
>
|
||||
<!-- 商品名称 -->
|
||||
<div
|
||||
v-if="property.fields.name.show"
|
||||
class="truncate text-12px"
|
||||
:style="{ color: property.fields.name.color }"
|
||||
{{ spu.name }}
|
||||
</div>
|
||||
<!-- 商品简介 -->
|
||||
<div
|
||||
v-if="property.fields.introduction.show"
|
||||
class="truncate text-12px"
|
||||
:style="{ color: property.fields.introduction.color }"
|
||||
>
|
||||
{{ spu.introduction }}
|
||||
</div>
|
||||
<div>
|
||||
<!-- 价格 -->
|
||||
<span
|
||||
v-if="property.fields.price.show"
|
||||
class="text-16px"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
>
|
||||
{{ spu.name }}
|
||||
</div>
|
||||
<div>
|
||||
<!-- 商品价格 -->
|
||||
<span
|
||||
v-if="property.fields.price.show"
|
||||
class="text-12px"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
>
|
||||
¥{{ spu.price }}
|
||||
</span>
|
||||
</div>
|
||||
¥{{ fenToYuan(spu.price || Infinity) }}
|
||||
</span>
|
||||
<!-- 市场价 -->
|
||||
<span
|
||||
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||
class="ml-4px text-10px line-through"
|
||||
:style="{ color: property.fields.marketPrice.color }"
|
||||
>¥{{ fenToYuan(spu.marketPrice) }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-12px">
|
||||
<!-- 销量 -->
|
||||
<span
|
||||
v-if="property.fields.salesCount.show"
|
||||
:style="{ color: property.fields.salesCount.color }"
|
||||
>
|
||||
已售{{ (spu.salesCount || 0) + (spu.virtualSalesCount || 0) }}件
|
||||
</span>
|
||||
<!-- 库存 -->
|
||||
<span v-if="property.fields.stock.show" :style="{ color: property.fields.stock.color }">
|
||||
库存{{ spu.stock || 0 }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 购买按钮 -->
|
||||
<div class="absolute bottom-8px right-8px">
|
||||
<!-- 文字按钮 -->
|
||||
<span
|
||||
v-if="property.btnBuy.type === 'text'"
|
||||
class="rounded-full p-x-12px p-y-4px text-12px text-white"
|
||||
:style="{
|
||||
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||
}"
|
||||
>
|
||||
{{ property.btnBuy.text }}
|
||||
</span>
|
||||
<!-- 图片按钮 -->
|
||||
<el-image
|
||||
v-else
|
||||
class="h-28px w-28px rounded-full"
|
||||
fit="cover"
|
||||
:src="property.btnBuy.imgUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { PromotionCombinationProperty } from './config'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
/** 拼团 */
|
||||
/** 拼团卡片 */
|
||||
defineOptions({ name: 'PromotionCombination' })
|
||||
// 定义属性
|
||||
const props = defineProps<{ property: PromotionCombinationProperty }>()
|
||||
// 商品列表
|
||||
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||
const spuIdList = ref<number[]>([])
|
||||
const combinationActivityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
||||
|
||||
watch(
|
||||
() => props.property.activityId,
|
||||
() => props.property.activityIds,
|
||||
async () => {
|
||||
if (!props.property.activityId) return
|
||||
const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId)
|
||||
if (!activity?.spuId) return
|
||||
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||
try {
|
||||
// 新添加的拼团组件,是没有活动ID的
|
||||
const activityIds = props.property.activityIds
|
||||
// 检查活动ID的有效性
|
||||
if (Array.isArray(activityIds) && activityIds.length > 0) {
|
||||
// 获取拼团活动详情列表
|
||||
combinationActivityList.value =
|
||||
await CombinationActivityApi.getCombinationActivityListByIds(activityIds)
|
||||
|
||||
// 获取拼团活动的 SPU 详情列表
|
||||
spuList.value = []
|
||||
spuIdList.value = combinationActivityList.value
|
||||
.map((activity) => activity.spuId)
|
||||
.filter((spuId): spuId is number => typeof spuId === 'number')
|
||||
if (spuIdList.value.length > 0) {
|
||||
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value)
|
||||
}
|
||||
|
||||
// 更新 SPU 的最低价格
|
||||
combinationActivityList.value.forEach((activity) => {
|
||||
// 匹配spuId
|
||||
const spu = spuList.value.find((spu) => spu.id === activity.spuId)
|
||||
if (spu) {
|
||||
// 赋值活动价格,哪个最便宜就赋值哪个
|
||||
spu.price = Math.min(activity.combinationPrice || Infinity, spu.price || Infinity)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取拼团活动细节或 SPU 细节时出错:', error)
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
// 手机宽度
|
||||
const phoneWidth = ref(375)
|
||||
|
||||
/**
|
||||
* 计算商品的间距
|
||||
* @param index 商品索引
|
||||
*/
|
||||
const calculateSpace = (index: number) => {
|
||||
// 商品的列数
|
||||
const columns = props.property.layoutType === 'twoCol' ? 2 : 1
|
||||
// 第一列没有左边距
|
||||
const marginLeft = index % columns === 0 ? '0' : props.property.space + 'px'
|
||||
// 第一行没有上边距
|
||||
const marginTop = index < columns ? '0' : props.property.space + 'px'
|
||||
|
||||
return { marginLeft, marginTop }
|
||||
}
|
||||
|
||||
// 容器
|
||||
const containerRef = ref()
|
||||
// 商品的列数
|
||||
const columns = ref(2)
|
||||
// 滚动条宽度
|
||||
const scrollbarWidth = ref('100%')
|
||||
// 商品图大小
|
||||
const imageSize = ref('0')
|
||||
// 商品网络列数
|
||||
const gridTemplateColumns = ref('')
|
||||
// 计算布局参数
|
||||
watch(
|
||||
() => [props.property, phoneWidth, spuList.value.length],
|
||||
() => {
|
||||
// 计算列数
|
||||
columns.value = props.property.layoutType === 'oneCol' ? 1 : 3
|
||||
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
||||
const productWidth =
|
||||
(phoneWidth.value - props.property.space * (columns.value - 1)) / columns.value
|
||||
// 商品图布局:2列时,左右布局 3列时,上下布局
|
||||
imageSize.value = columns.value === 2 ? '64px' : `${productWidth}px`
|
||||
// 指定列数
|
||||
gridTemplateColumns.value = `repeat(${columns.value}, auto)`
|
||||
// 不滚动
|
||||
scrollbarWidth.value = '100%'
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
onMounted(() => {
|
||||
// 提取手机宽度
|
||||
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
|
||||
})
|
||||
// 计算商品的宽度
|
||||
const calculateWidth = () => {
|
||||
let width = '100%'
|
||||
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
|
||||
if (props.property.layoutType === 'twoCol') {
|
||||
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`
|
||||
}
|
||||
return { width }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
|
@ -2,30 +2,31 @@
|
|||
<ComponentContainerProperty v-model="formData.style">
|
||||
<el-form label-width="80px" :model="formData">
|
||||
<el-card header="拼团活动" class="property-group" shadow="never">
|
||||
<el-form-item label="拼团活动" prop="activityId">
|
||||
<el-select v-model="formData.activityId">
|
||||
<el-option
|
||||
v-for="activity in activityList"
|
||||
:key="activity.id"
|
||||
:label="activity.name"
|
||||
:value="activity.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<CombinationShowcase v-model="formData.activityIds" />
|
||||
</el-card>
|
||||
<el-card header="商品样式" class="property-group" shadow="never">
|
||||
<el-form-item label="布局" prop="type">
|
||||
<el-radio-group v-model="formData.layoutType">
|
||||
<el-tooltip class="item" content="单列" placement="bottom">
|
||||
<el-radio-button label="oneCol">
|
||||
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||
<el-radio-button value="oneColBigImg">
|
||||
<Icon icon="fluent:text-column-one-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button label="threeCol">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||
<el-radio-button value="oneColSmallImg">
|
||||
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="双列" placement="bottom">
|
||||
<el-radio-button value="twoCol">
|
||||
<Icon icon="fluent:text-column-two-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<!--<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button value="threeCol">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>-->
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品名称" prop="fields.name.show">
|
||||
|
@ -34,12 +35,36 @@
|
|||
<el-checkbox v-model="formData.fields.name.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品简介" prop="fields.introduction.show">
|
||||
<div class="flex gap-8px">
|
||||
<ColorInput v-model="formData.fields.introduction.color" />
|
||||
<el-checkbox v-model="formData.fields.introduction.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品价格" prop="fields.price.show">
|
||||
<div class="flex gap-8px">
|
||||
<ColorInput v-model="formData.fields.price.color" />
|
||||
<el-checkbox v-model="formData.fields.price.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="市场价" prop="fields.marketPrice.show">
|
||||
<div class="flex gap-8px">
|
||||
<ColorInput v-model="formData.fields.marketPrice.color" />
|
||||
<el-checkbox v-model="formData.fields.marketPrice.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品销量" prop="fields.salesCount.show">
|
||||
<div class="flex gap-8px">
|
||||
<ColorInput v-model="formData.fields.salesCount.color" />
|
||||
<el-checkbox v-model="formData.fields.salesCount.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品库存" prop="fields.stock.show">
|
||||
<div class="flex gap-8px">
|
||||
<ColorInput v-model="formData.fields.stock.color" />
|
||||
<el-checkbox v-model="formData.fields.stock.show" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card header="角标" class="property-group" shadow="never">
|
||||
<el-form-item label="角标" prop="badge.show">
|
||||
|
@ -47,10 +72,36 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
|
||||
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
|
||||
<template #tip> 建议尺寸:36 * 22 </template>
|
||||
<template #tip> 建议尺寸:36 * 22</template>
|
||||
</UploadImg>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card header="按钮" class="property-group" shadow="never">
|
||||
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||
<el-radio-group v-model="formData.btnBuy.type">
|
||||
<el-radio-button value="text">文字</el-radio-button>
|
||||
<el-radio-button value="img">图片</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<template v-if="formData.btnBuy.type === 'text'">
|
||||
<el-form-item label="按钮文字" prop="btnBuy.text">
|
||||
<el-input v-model="formData.btnBuy.text" />
|
||||
</el-form-item>
|
||||
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
|
||||
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
|
||||
<ColorInput v-model="formData.btnBuy.bgEndColor" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item label="图片" prop="btnBuy.imgUrl">
|
||||
<UploadImg v-model="formData.btnBuy.imgUrl" height="56px" width="56px">
|
||||
<template #tip> 建议尺寸:56 * 56</template>
|
||||
</UploadImg>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-card>
|
||||
<el-card header="商品样式" class="property-group" shadow="never">
|
||||
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||
<el-slider
|
||||
|
@ -92,6 +143,7 @@ import { PromotionCombinationProperty } from './config'
|
|||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import CombinationShowcase from '@/views/mall/promotion/combination/components/CombinationShowcase.vue'
|
||||
|
||||
// 拼团属性面板
|
||||
defineOptions({ name: 'PromotionCombinationProperty' })
|
||||
|
@ -100,7 +152,7 @@ const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
|||
const emit = defineEmits(['update:modelValue'])
|
||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||
// 活动列表
|
||||
const activityList = ref<CombinationActivityApi.CombinationActivityVO>([])
|
||||
const activityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
||||
onMounted(async () => {
|
||||
const { list } = await CombinationActivityApi.getCombinationActivityPage({
|
||||
status: CommonStatusEnum.ENABLE
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
<template>
|
||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
||||
<el-scrollbar ref="containerRef" class="z-1 min-h-30px" wrap-class="w-full">
|
||||
<!-- 商品网格 -->
|
||||
<div
|
||||
class="grid overflow-x-auto"
|
||||
:style="{
|
||||
gridGap: `${property.space}px`,
|
||||
gridTemplateColumns,
|
||||
width: scrollbarWidth
|
||||
}"
|
||||
class="grid overflow-x-auto"
|
||||
>
|
||||
<!-- 商品 -->
|
||||
<div
|
||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
v-for="(spu, index) in spuList"
|
||||
:key="index"
|
||||
:style="{
|
||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||
}"
|
||||
v-for="(spu, index) in spuList"
|
||||
:key="index"
|
||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
>
|
||||
<!-- 角标 -->
|
||||
<div
|
||||
v-if="property.badge.show"
|
||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||
>
|
||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||
<el-image :src="property.badge.imgUrl" class="h-26px w-38px" fit="cover" />
|
||||
</div>
|
||||
<!-- 商品封面图 -->
|
||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
||||
<el-image :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" fit="cover" />
|
||||
<div
|
||||
:class="[
|
||||
'flex flex-col gap-8px p-8px box-border',
|
||||
|
@ -42,8 +42,8 @@
|
|||
<!-- 商品名称 -->
|
||||
<div
|
||||
v-if="property.fields.name.show"
|
||||
class="truncate text-12px"
|
||||
:style="{ color: property.fields.name.color }"
|
||||
class="truncate text-12px"
|
||||
>
|
||||
{{ spu.name }}
|
||||
</div>
|
||||
|
@ -51,10 +51,10 @@
|
|||
<!-- 商品价格 -->
|
||||
<span
|
||||
v-if="property.fields.price.show"
|
||||
class="text-12px"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
class="text-12px"
|
||||
>
|
||||
¥{{ spu.price }}
|
||||
¥{{ fenToYuan(spu.seckillPrice || spu.price || 0) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -62,10 +62,13 @@
|
|||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { PromotionSeckillProperty } from './config'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import { Spu } from '@/api/mall/product/spu'
|
||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||
import { SeckillProductVO } from '@/api/mall/promotion/seckill/seckillActivity'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
/** 秒杀 */
|
||||
defineOptions({ name: 'PromotionSeckill' })
|
||||
|
@ -80,6 +83,13 @@ watch(
|
|||
const activity = await SeckillActivityApi.getSeckillActivity(props.property.activityId)
|
||||
if (!activity?.spuId) return
|
||||
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||
// 循环活动信息,赋值秒杀最低价格
|
||||
activity.products.forEach((product: SeckillProductVO) => {
|
||||
spuList.value.forEach((spu: Spu) => {
|
||||
spu.seckillPrice = Math.min(spu.seckillPrice || Infinity, product.seckillPrice) // 设置 SPU 的最低价格
|
||||
})
|
||||
})
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -122,4 +132,4 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<el-form-item label="布局" prop="type">
|
||||
<el-radio-group v-model="formData.layoutType">
|
||||
<el-tooltip class="item" content="单列" placement="bottom">
|
||||
<el-radio-button label="oneCol">
|
||||
<el-radio-button value="oneCol">
|
||||
<Icon icon="fluent:text-column-one-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button label="threeCol">
|
||||
<el-radio-button value="threeCol">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
<el-form-item label="框体样式">
|
||||
<el-radio-group v-model="formData!.borderRadius">
|
||||
<el-tooltip content="方形" placement="top">
|
||||
<el-radio-button :label="0">
|
||||
<el-radio-button :value="0">
|
||||
<Icon icon="tabler:input-search" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="圆形" placement="top">
|
||||
<el-radio-button :label="10">
|
||||
<el-radio-button :value="10">
|
||||
<Icon icon="iconoir:input-search" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
@ -30,12 +30,12 @@
|
|||
<el-form-item label="文本位置" prop="placeholderPosition">
|
||||
<el-radio-group v-model="formData!.placeholderPosition">
|
||||
<el-tooltip content="居左" placement="top">
|
||||
<el-radio-button label="left">
|
||||
<el-radio-button value="left">
|
||||
<Icon icon="ant-design:align-left-outlined" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="居中" placement="top">
|
||||
<el-radio-button label="center">
|
||||
<el-radio-button value="center">
|
||||
<Icon icon="ant-design:align-center-outlined" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="导航背景">
|
||||
<el-radio-group v-model="formData!.style.bgType">
|
||||
<el-radio-button label="color">纯色</el-radio-button>
|
||||
<el-radio-button label="img">图片</el-radio-button>
|
||||
<el-radio-button value="color">纯色</el-radio-button>
|
||||
<el-radio-button value="img">图片</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'">
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
<el-form-item label="标题位置" prop="textAlign">
|
||||
<el-radio-group v-model="formData!.textAlign">
|
||||
<el-tooltip content="居左" placement="top">
|
||||
<el-radio-button label="left">
|
||||
<el-radio-button value="left">
|
||||
<Icon icon="ant-design:align-left-outlined" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="居中" placement="top">
|
||||
<el-radio-button label="center">
|
||||
<el-radio-button value="center">
|
||||
<Icon icon="ant-design:align-center-outlined" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
|
@ -88,9 +88,9 @@
|
|||
<template v-if="formData.more.show">
|
||||
<el-form-item label="样式" prop="more.type">
|
||||
<el-radio-group v-model="formData.more.type">
|
||||
<el-radio label="text">文字</el-radio>
|
||||
<el-radio label="icon">图标</el-radio>
|
||||
<el-radio label="all">文字+图标</el-radio>
|
||||
<el-radio value="text">文字</el-radio>
|
||||
<el-radio value="icon">图标</el-radio>
|
||||
<el-radio value="all">文字+图标</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="更多文字" prop="more.text" v-show="formData.more.type !== 'icon'">
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
|
||||
>
|
||||
<!-- 操作按钮区 -->
|
||||
<div class="m--8px m-b-4px flex flex-row items-center justify-between bg-gray-1 p-8px">
|
||||
<div class="m--8px m-b-4px flex flex-row items-center justify-between p-8px" style="background-color: var(--app-content-bg-color);">
|
||||
<el-tooltip content="拖动排序">
|
||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" style="color: #8a909c;" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除">
|
||||
<Icon
|
||||
|
|
|
@ -7,26 +7,41 @@ const props = defineProps({
|
|||
src: propTypes.string.def('')
|
||||
})
|
||||
const loading = ref(true)
|
||||
const height = ref('')
|
||||
const frameRef = ref<HTMLElement | null>(null)
|
||||
const init = () => {
|
||||
height.value = document.documentElement.clientHeight - 94.5 + 'px'
|
||||
loading.value = false
|
||||
nextTick(() => {
|
||||
loading.value = true
|
||||
if (!frameRef.value) return
|
||||
frameRef.value.onload = () => {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
init()
|
||||
}, 300)
|
||||
init()
|
||||
})
|
||||
watch(
|
||||
() => props.src,
|
||||
() => {
|
||||
init()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<div v-loading="loading" :style="'height:' + height">
|
||||
<div
|
||||
v-loading="loading"
|
||||
class="w-full h-[calc(100vh-var(--top-tool-height)-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-2px)]"
|
||||
>
|
||||
<iframe
|
||||
ref="frameRef"
|
||||
:src="props.src"
|
||||
frameborder="no"
|
||||
frameborder="0"
|
||||
scrolling="auto"
|
||||
style="width: 100%; height: 100%"
|
||||
height="100%"
|
||||
width="100%"
|
||||
allowfullscreen="true"
|
||||
webkitallowfullscreen="true"
|
||||
mozallowfullscreen="true"
|
||||
></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -11,6 +11,10 @@ const props = defineProps({
|
|||
modelValue: {
|
||||
require: false,
|
||||
type: String
|
||||
},
|
||||
clearable: {
|
||||
require: false,
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
const emit = defineEmits<{ (e: 'update:modelValue', v: string) }>()
|
||||
|
@ -92,6 +96,12 @@ function onCurrentChange(page) {
|
|||
currentPage.value = page
|
||||
}
|
||||
|
||||
function clearIcon() {
|
||||
icon.value = ''
|
||||
emit('update:modelValue', '')
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue
|
||||
|
@ -115,14 +125,14 @@ watch(
|
|||
|
||||
<template>
|
||||
<div class="selector">
|
||||
<ElInput v-model="inputValue" @click="visible = !visible">
|
||||
<ElInput v-model="inputValue" @click="visible = !visible" :clearable="props.clearable" @clear="clearIcon">
|
||||
<template #append>
|
||||
<ElPopover
|
||||
:popper-options="{
|
||||
placement: 'auto'
|
||||
}"
|
||||
:visible="visible"
|
||||
:width="350"
|
||||
:width="355"
|
||||
popper-class="pure-popper"
|
||||
trigger="click"
|
||||
>
|
||||
|
@ -147,7 +157,7 @@ watch(
|
|||
>
|
||||
<ElDivider border-style="dashed" class="tab-divider" />
|
||||
<ElScrollbar height="220px">
|
||||
<ul class="ml-2 flex flex-wrap px-2">
|
||||
<ul class="ml-2 flex flex-wrap">
|
||||
<li
|
||||
v-for="(item, key) in pageList"
|
||||
:key="key"
|
||||
|
@ -171,7 +181,7 @@ watch(
|
|||
background
|
||||
class="h-10 flex items-center justify-center"
|
||||
layout="prev, pager, next"
|
||||
small
|
||||
size="small"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
</ElPopover>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<el-radio-group v-model="shortcutDays" @change="handleShortcutDaysChange">
|
||||
<el-radio-button :label="1">昨天</el-radio-button>
|
||||
<el-radio-button :label="7">最近7天</el-radio-button>
|
||||
<el-radio-button :label="30">最近30天</el-radio-button>
|
||||
<el-radio-button :value="1">昨天</el-radio-button>
|
||||
<el-radio-button :value="7">最近7天</el-radio-button>
|
||||
<el-radio-button :value="30">最近30天</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-date-picker
|
||||
v-model="times"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="upload-file">
|
||||
<div v-if="!disabled" class="upload-file">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
v-model:file-list="fileList"
|
||||
|
@ -20,11 +20,11 @@
|
|||
class="upload-file-uploader"
|
||||
name="file"
|
||||
>
|
||||
<el-button v-if="!disabled" type="primary">
|
||||
<el-button type="primary">
|
||||
<Icon icon="ep:upload-filled" />
|
||||
选取文件
|
||||
</el-button>
|
||||
<template v-if="isShowTip && !disabled" #tip>
|
||||
<template v-if="isShowTip" #tip>
|
||||
<div style="font-size: 8px">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</div>
|
||||
|
@ -32,7 +32,6 @@
|
|||
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件
|
||||
</div>
|
||||
</template>
|
||||
<!-- TODO @puhui999:1)表单展示的时候,位置会偏掉,已发微信;2)disable 的时候,应该把【删除】按钮也隐藏掉? -->
|
||||
<template #file="row">
|
||||
<div class="flex items-center">
|
||||
<span>{{ row.file.name }}</span>
|
||||
|
@ -54,6 +53,18 @@
|
|||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<!-- 上传操作禁用时 -->
|
||||
<div v-if="disabled" class="upload-file">
|
||||
<div v-for="(file, index) in fileList" :key="index" class="flex items-center file-list-item">
|
||||
<span>{{ file.name }}</span>
|
||||
<div class="ml-10px">
|
||||
<el-link :href="file.url" :underline="false" download target="_blank" type="primary">
|
||||
下载
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
@ -211,4 +222,9 @@ const emitUpdateModelValue = () => {
|
|||
:deep(.ele-upload-list__item-content-action .el-link) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.file-list-item {
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -45,17 +45,20 @@
|
|||
<el-checkbox
|
||||
v-model="loopInstanceForm.asyncBefore"
|
||||
label="异步前"
|
||||
value="异步前"
|
||||
@change="updateLoopAsync('asyncBefore')"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-model="loopInstanceForm.asyncAfter"
|
||||
label="异步后"
|
||||
value="异步后"
|
||||
@change="updateLoopAsync('asyncAfter')"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-model="loopInstanceForm.exclusive"
|
||||
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
|
||||
label="排除"
|
||||
value="排除"
|
||||
@change="updateLoopAsync('exclusive')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
|
@ -6,13 +6,20 @@
|
|||
<el-checkbox
|
||||
v-model="taskConfigForm.asyncBefore"
|
||||
label="异步前"
|
||||
value="异步前"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-model="taskConfigForm.asyncAfter"
|
||||
label="异步后"
|
||||
value="异步后"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
<el-checkbox v-model="taskConfigForm.asyncAfter" label="异步后" @change="changeTaskAsync" />
|
||||
<el-checkbox
|
||||
v-model="taskConfigForm.exclusive"
|
||||
v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore"
|
||||
label="排除"
|
||||
value="排除"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
|
@ -206,15 +206,12 @@ const refreshToken = async () => {
|
|||
const handleAuthorized = () => {
|
||||
const { t } = useI18n()
|
||||
if (!isRelogin.show) {
|
||||
// 如果已经到重新登录页面则不进行弹窗提示
|
||||
if (window.location.href.includes('login?redirect=')) {
|
||||
return
|
||||
}
|
||||
isRelogin.show = true
|
||||
ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
|
||||
showCancelButton: false,
|
||||
closeOnClickModal: false,
|
||||
showClose: false,
|
||||
closeOnPressEscape: false,
|
||||
confirmButtonText: t('login.relogin'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
|
|
|
@ -71,8 +71,5 @@ $prefix-cls: #{$namespace}-layout;
|
|||
|
||||
.#{$prefix-cls} {
|
||||
background-color: var(--app-content-bg-color);
|
||||
:deep(.#{$elNamespace}-scrollbar__view) {
|
||||
height: 99% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -36,27 +36,10 @@ provide('reload', reload)
|
|||
<template>
|
||||
<section
|
||||
:class="[
|
||||
'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
|
||||
'p-[var(--app-content-padding)] w-full bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
|
||||
{
|
||||
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
(fixedHeader &&
|
||||
(layout === 'classic' || layout === 'topLeft' || layout === 'top') &&
|
||||
footer) ||
|
||||
(!tagsView && layout === 'top' && footer),
|
||||
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
|
||||
tagsView && layout === 'top' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'classic' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'topLeft' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
|
||||
fixedHeader && layout === 'cutMenu' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
|
||||
!fixedHeader && layout === 'cutMenu' && footer
|
||||
'!min-h-[calc(100vh-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))] pb-0':
|
||||
footer
|
||||
}
|
||||
]"
|
||||
>
|
||||
|
|
|
@ -17,7 +17,7 @@ const title = computed(() => appStore.getTitle)
|
|||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)]"
|
||||
class="h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)] overflow-hidden"
|
||||
>
|
||||
<span class="text-14px">Copyright ©2022-{{ title }}</span>
|
||||
</div>
|
||||
|
|
|
@ -195,6 +195,16 @@ $prefix-cls: #{$namespace}-menu;
|
|||
}
|
||||
}
|
||||
|
||||
// 垂直菜单
|
||||
&__vertical {
|
||||
:deep(.#{$elNamespace}-menu--vertical) {
|
||||
&:not(.#{$elNamespace}-menu--collapse) .#{$elNamespace}-sub-menu__title,
|
||||
.#{$elNamespace}-menu-item {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 水平菜单
|
||||
&__horizontal {
|
||||
height: calc(var(--top-tool-height)) !important;
|
||||
|
|
|
@ -126,8 +126,10 @@ const copyConfig = async () => {
|
|||
message: ${appStore.getMessage},
|
||||
// 标签页
|
||||
tagsView: ${appStore.getTagsView},
|
||||
// 标签页
|
||||
tagsViewImmerse: ${appStore.getTagsViewImmerse},
|
||||
// 标签页图标
|
||||
getTagsViewIcon: ${appStore.getTagsViewIcon},
|
||||
tagsViewIcon: ${appStore.getTagsViewIcon},
|
||||
// logo
|
||||
logo: ${appStore.getLogo},
|
||||
// 菜单手风琴
|
||||
|
|
|
@ -73,6 +73,13 @@ const tagsViewChange = (show: boolean) => {
|
|||
appStore.setTagsView(show)
|
||||
}
|
||||
|
||||
// 标签页沉浸
|
||||
const tagsViewImmerse = ref(appStore.getTagsViewImmerse)
|
||||
|
||||
const tagsViewImmerseChange = (immerse: boolean) => {
|
||||
appStore.setTagsViewImmerse(immerse)
|
||||
}
|
||||
|
||||
// 标签页图标
|
||||
const tagsViewIcon = ref(appStore.getTagsViewIcon)
|
||||
|
||||
|
@ -181,6 +188,11 @@ watch(
|
|||
<ElSwitch v-model="tagsView" @change="tagsViewChange" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-14px">{{ t('setting.tagsViewImmerse') }}</span>
|
||||
<ElSwitch v-model="tagsViewImmerse" @change="tagsViewImmerseChange" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-14px">{{ t('setting.tagsViewIcon') }}</span>
|
||||
<ElSwitch v-model="tagsViewIcon" @change="tagsViewIconChange" />
|
||||
|
|
|
@ -139,7 +139,7 @@ export default defineComponent({
|
|||
id={`${variables.namespace}-menu`}
|
||||
class={[
|
||||
prefixCls,
|
||||
'relative bg-[var(--left-menu-bg-color)] top-1px layout-border__right',
|
||||
'relative bg-[var(--left-menu-bg-color)] layout-border__right',
|
||||
{
|
||||
'w-[var(--tab-menu-max-width)]': !unref(collapse),
|
||||
'w-[var(--tab-menu-min-width)]': unref(collapse)
|
||||
|
@ -147,7 +147,7 @@ export default defineComponent({
|
|||
]}
|
||||
onMouseleave={mouseleave}
|
||||
>
|
||||
<ElScrollbar class="!h-[calc(100%-var(--tab-menu-collapse-height)-1px)]">
|
||||
<ElScrollbar class="!h-[calc(100%-var(--tab-menu-collapse-height))]">
|
||||
<div>
|
||||
{() => {
|
||||
return unref(tabRouters).map((v) => {
|
||||
|
@ -199,7 +199,7 @@ export default defineComponent({
|
|||
{
|
||||
'!left-[var(--tab-menu-min-width)]': unref(collapse),
|
||||
'!left-[var(--tab-menu-max-width)]': !unref(collapse),
|
||||
'!w-[calc(var(--left-menu-max-width)+1px)]': unref(showMenu) || unref(fixedMenu),
|
||||
'!w-[var(--left-menu-max-width)]': unref(showMenu) || unref(fixedMenu),
|
||||
'!w-0': !unref(showMenu) && !unref(fixedMenu)
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, watch, computed, unref, ref, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { computed, nextTick, onMounted, ref, unref, watch } from 'vue'
|
||||
import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
@ -33,6 +33,8 @@ const affixTagArr = ref<RouteLocationNormalizedLoaded[]>([])
|
|||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const tagsViewImmerse = computed(() => appStore.getTagsViewImmerse)
|
||||
|
||||
const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
|
||||
|
||||
const isDark = computed(() => appStore.getIsDark)
|
||||
|
@ -266,21 +268,33 @@ watch(
|
|||
class="relative w-full flex bg-[#fff] dark:bg-[var(--el-bg-color)]"
|
||||
>
|
||||
<span
|
||||
:class="`${prefixCls}__tool ${prefixCls}__tool--first`"
|
||||
:class="tagsViewImmerse ? '' : `${prefixCls}__tool ${prefixCls}__tool--first`"
|
||||
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
|
||||
@click="move(-200)"
|
||||
>
|
||||
<Icon
|
||||
icon="ep:d-arrow-left"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
icon="ep:d-arrow-left"
|
||||
/>
|
||||
</span>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
|
||||
<div class="h-full flex">
|
||||
<div class="h-[var(--tags-view-height)] flex">
|
||||
<ContextMenu
|
||||
v-for="item in visitedViews"
|
||||
:key="item.fullPath"
|
||||
:ref="itemRefs.set"
|
||||
:class="[
|
||||
`${prefixCls}__item`,
|
||||
tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
|
||||
tagsViewIcon ? `${prefixCls}__item--icon` : '',
|
||||
tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
|
||||
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
|
||||
{
|
||||
'is-active': isActive(item)
|
||||
}
|
||||
]"
|
||||
:schema="[
|
||||
{
|
||||
icon: 'ep:refresh',
|
||||
|
@ -338,41 +352,33 @@ watch(
|
|||
}
|
||||
}
|
||||
]"
|
||||
v-for="item in visitedViews"
|
||||
:key="item.fullPath"
|
||||
:tag-item="item"
|
||||
:class="[
|
||||
`${prefixCls}__item`,
|
||||
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
|
||||
{
|
||||
'is-active': isActive(item)
|
||||
}
|
||||
]"
|
||||
@visible-change="visibleChange"
|
||||
>
|
||||
<div>
|
||||
<router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }">
|
||||
<router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
|
||||
<div
|
||||
:class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
|
||||
@click="navigate"
|
||||
class="h-full flex items-center justify-center whitespace-nowrap pl-15px"
|
||||
>
|
||||
<Icon
|
||||
v-if="
|
||||
item?.matched &&
|
||||
item?.matched[1] &&
|
||||
item?.matched[1]?.meta?.icon &&
|
||||
tagsViewIcon
|
||||
tagsViewIcon &&
|
||||
(item?.meta?.icon ||
|
||||
(item?.matched &&
|
||||
item.matched[0] &&
|
||||
item.matched[item.matched.length - 1].meta?.icon))
|
||||
"
|
||||
:icon="item?.matched[1]?.meta?.icon"
|
||||
:icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
|
||||
:size="12"
|
||||
class="mr-5px"
|
||||
/>
|
||||
{{ t(item?.meta?.title as string) }}
|
||||
<Icon
|
||||
:class="`${prefixCls}__item--close`"
|
||||
:size="12"
|
||||
color="#333"
|
||||
icon="ep:close"
|
||||
:size="12"
|
||||
@click.prevent.stop="closeSelectedTag(item)"
|
||||
/>
|
||||
</div>
|
||||
|
@ -383,29 +389,28 @@ watch(
|
|||
</ElScrollbar>
|
||||
</div>
|
||||
<span
|
||||
:class="`${prefixCls}__tool`"
|
||||
:class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
|
||||
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
|
||||
@click="move(200)"
|
||||
>
|
||||
<Icon
|
||||
icon="ep:d-arrow-right"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
icon="ep:d-arrow-right"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
:class="`${prefixCls}__tool`"
|
||||
:class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
|
||||
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
|
||||
@click="refreshSelectedTag(selectedTag)"
|
||||
>
|
||||
<Icon
|
||||
icon="ep:refresh-right"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
icon="ep:refresh-right"
|
||||
/>
|
||||
</span>
|
||||
<ContextMenu
|
||||
trigger="click"
|
||||
:schema="[
|
||||
{
|
||||
icon: 'ep:refresh',
|
||||
|
@ -457,15 +462,16 @@ watch(
|
|||
}
|
||||
}
|
||||
]"
|
||||
trigger="click"
|
||||
>
|
||||
<span
|
||||
:class="`${prefixCls}__tool`"
|
||||
:class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
|
||||
class="block h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
|
||||
>
|
||||
<Icon
|
||||
icon="ep:menu"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
icon="ep:menu"
|
||||
/>
|
||||
</span>
|
||||
</ContextMenu>
|
||||
|
@ -485,10 +491,10 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 1px);
|
||||
height: 100%;
|
||||
border-left: 1px solid var(--el-border-color);
|
||||
content: '';
|
||||
}
|
||||
|
@ -496,10 +502,10 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
&--first {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 1px);
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--el-border-color);
|
||||
border-left: none;
|
||||
content: '';
|
||||
|
@ -509,14 +515,15 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
|
||||
&__item {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
top: 3px;
|
||||
height: calc(100% - 6px);
|
||||
padding-right: 25px;
|
||||
padding-right: 15px;
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&--close {
|
||||
position: absolute;
|
||||
|
@ -525,6 +532,7 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
display: none;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
&:not(.#{$prefix-cls}__item--affix):hover {
|
||||
.#{$prefix-cls}__item--close {
|
||||
display: block;
|
||||
|
@ -532,6 +540,10 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
}
|
||||
}
|
||||
|
||||
&__item--icon {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&__item:not(.is-active) {
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
|
@ -542,12 +554,48 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
color: var(--el-color-white);
|
||||
background-color: var(--el-color-primary);
|
||||
border: 1px solid var(--el-color-primary);
|
||||
|
||||
.#{$prefix-cls}__item--close {
|
||||
:deep(span) {
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__item--immerse {
|
||||
top: 2px;
|
||||
height: calc(100% - 3px);
|
||||
padding-right: 35px;
|
||||
margin: 0 -10px;
|
||||
border: none !important;
|
||||
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E")
|
||||
12 27 15;
|
||||
|
||||
.#{$prefix-cls}__item--label {
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.#{$prefix-cls}__item--close {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__item--immerse--icon {
|
||||
padding-right: 35px;
|
||||
}
|
||||
|
||||
&__item--immerse:not(.is-active) {
|
||||
&:hover {
|
||||
color: var(--el-color-white);
|
||||
background-color: var(--el-color-primary);
|
||||
|
||||
.#{$prefix-cls}__item--close {
|
||||
:deep(span) {
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
@ -574,12 +622,19 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||
color: var(--el-color-white);
|
||||
background-color: var(--el-color-primary);
|
||||
border: 1px solid var(--el-color-primary);
|
||||
|
||||
.#{$prefix-cls}__item--close {
|
||||
:deep(span) {
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__item--immerse:not(.is-active) {
|
||||
&:hover {
|
||||
color: var(--el-color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -23,7 +23,7 @@ const { getPrefixCls } = useDesign()
|
|||
|
||||
const prefixCls = getPrefixCls('user-info')
|
||||
|
||||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
|
||||
const avatar = computed(() => userStore.user.avatar || avatarImg)
|
||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
|
||||
|
||||
// 锁定屏幕
|
||||
|
|
|
@ -21,7 +21,7 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
|
||||
const avatar = computed(() => userStore.user.avatar || avatarImg)
|
||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
|
|
@ -22,7 +22,7 @@ const showDate = ref(true)
|
|||
const { getPrefixCls } = useDesign()
|
||||
const prefixCls = getPrefixCls('lock-page')
|
||||
|
||||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
|
||||
const avatar = computed(() => userStore.user.avatar || avatarImg)
|
||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
|
||||
|
||||
const lockStore = useLockStore()
|
||||
|
|
|
@ -126,7 +126,7 @@ export const useRenderLayout = () => {
|
|||
|
||||
<ToolHeader class="flex-1"></ToolHeader>
|
||||
</div>
|
||||
<div class="absolute left-0 top-[var(--logo-height)+1px] h-[calc(100%-1px-var(--logo-height))] w-full flex">
|
||||
<div class="absolute left-0 top-[var(--logo-height)] h-[calc(100%-var(--logo-height))] w-full flex">
|
||||
<Menu class="relative layout-border__right !h-full"></Menu>
|
||||
<div
|
||||
class={[
|
||||
|
@ -157,9 +157,9 @@ export const useRenderLayout = () => {
|
|||
'layout-border__bottom absolute',
|
||||
{
|
||||
'!fixed top-0 left-0 z-10': fixedHeader.value,
|
||||
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)] mt-[calc(var(--logo-height)+1px)]':
|
||||
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)] mt-[var(--logo-height)]':
|
||||
collapse.value && fixedHeader.value,
|
||||
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)] mt-[calc(var(--logo-height)+1px)]':
|
||||
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)] mt-[var(--logo-height)]':
|
||||
!collapse.value && fixedHeader.value
|
||||
}
|
||||
]}
|
||||
|
@ -190,24 +190,14 @@ export const useRenderLayout = () => {
|
|||
<Menu class="h-[var(--top-tool-height)] flex-1 px-10px"></Menu>
|
||||
<ToolHeader></ToolHeader>
|
||||
</div>
|
||||
<div
|
||||
class={[
|
||||
`${prefixCls}-content`,
|
||||
'w-full',
|
||||
{
|
||||
'h-[calc(100%-var(--app-footer-height))]': !fixedHeader.value,
|
||||
'h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]': fixedHeader.value
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div class={[`${prefixCls}-content`, 'w-full h-[calc(100%-var(--top-tool-height))]']}>
|
||||
<ElScrollbar
|
||||
v-loading={pageLoading.value}
|
||||
class={[
|
||||
`${prefixCls}-content-scrollbar`,
|
||||
{
|
||||
'mt-[var(--tags-view-height)] !pb-[calc(var(--tags-view-height)+var(--app-footer-height))]':
|
||||
fixedHeader.value,
|
||||
'pb-[var(--app-footer-height)]': !fixedHeader.value
|
||||
'!h-[calc(100%-var(--tags-view-height))] mt-[calc(var(--tags-view-height))]':
|
||||
fixedHeader.value
|
||||
}
|
||||
]}
|
||||
>
|
||||
|
@ -216,7 +206,7 @@ export const useRenderLayout = () => {
|
|||
class={[
|
||||
'layout-border__bottom layout-border__top relative',
|
||||
{
|
||||
'!fixed w-full top-[calc(var(--top-tool-height)+1px)] left-0': fixedHeader.value
|
||||
'!fixed w-full top-[var(--top-tool-height)] left-0': fixedHeader.value
|
||||
}
|
||||
]}
|
||||
style="transition: width var(--transition-time-02), left var(--transition-time-02);"
|
||||
|
@ -238,7 +228,7 @@ export const useRenderLayout = () => {
|
|||
|
||||
<ToolHeader class="flex-1"></ToolHeader>
|
||||
</div>
|
||||
<div class="absolute left-0 top-[var(--logo-height)] h-[calc(100%-var(--logo-height))] w-[calc(100%-2px)] flex">
|
||||
<div class="absolute left-0 top-[var(--logo-height)] h-[calc(100%-var(--logo-height))] w-full flex">
|
||||
<TabMenu></TabMenu>
|
||||
<div
|
||||
class={[
|
||||
|
@ -270,18 +260,16 @@ export const useRenderLayout = () => {
|
|||
{tagsView.value ? (
|
||||
<TagsView
|
||||
class={[
|
||||
'relative layout-border__bottom layout-border__top',
|
||||
'relative layout-border__bottom',
|
||||
{
|
||||
'!fixed top-0 left-0 z-10': fixedHeader.value,
|
||||
'w-[calc(100%-var(--tab-menu-min-width))] !left-[var(--tab-menu-min-width)] mt-[var(--logo-height)]':
|
||||
collapse.value && fixedHeader.value,
|
||||
collapse.value && fixedHeader.value && !fixedMenu.value,
|
||||
'w-[calc(100%-var(--tab-menu-max-width))] !left-[var(--tab-menu-max-width)] mt-[var(--logo-height)]':
|
||||
!collapse.value && fixedHeader.value,
|
||||
'!fixed top-0 !left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] z-10':
|
||||
fixedHeader.value && fixedMenu.value,
|
||||
'w-[calc(100%-var(--tab-menu-min-width)-var(--left-menu-max-width))] !left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
|
||||
!collapse.value && fixedHeader.value && !fixedMenu.value,
|
||||
'w-[calc(100%-var(--tab-menu-min-width)-var(--left-menu-max-width))] !left-[calc(var(--tab-menu-min-width)+var(--left-menu-max-width))] mt-[var(--logo-height)]':
|
||||
collapse.value && fixedHeader.value && fixedMenu.value,
|
||||
'w-[calc(100%-var(--tab-menu-max-width)-var(--left-menu-max-width))] !left-[var(--tab-menu-max-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
|
||||
'w-[calc(100%-var(--tab-menu-max-width)-var(--left-menu-max-width))] !left-[calc(var(--tab-menu-max-width)+var(--left-menu-max-width))] mt-[var(--logo-height)]':
|
||||
!collapse.value && fixedHeader.value && fixedMenu.value
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -92,6 +92,7 @@ export default {
|
|||
localeIcon: '多语言图标',
|
||||
messageIcon: '消息图标',
|
||||
tagsView: '标签页',
|
||||
tagsViewImmerse: '标签页沉浸',
|
||||
logo: '标志',
|
||||
greyMode: '灰色模式',
|
||||
fixedHeader: '固定头部',
|
||||
|
|
|
@ -593,6 +593,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/Error/404.vue'),
|
||||
name: '',
|
||||
meta: {
|
||||
title: '404',
|
||||
hidden: true,
|
||||
breadcrumb: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { setCssVar, humpToUnderline } from '@/utils'
|
||||
import { humpToUnderline, setCssVar } from '@/utils'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
import { ElementPlusSize } from '@/types/elementPlus'
|
||||
|
@ -21,6 +21,7 @@ interface AppState {
|
|||
locale: boolean
|
||||
message: boolean
|
||||
tagsView: boolean
|
||||
tagsViewImmerse: boolean
|
||||
tagsViewIcon: boolean
|
||||
logo: boolean
|
||||
fixedHeader: boolean
|
||||
|
@ -58,6 +59,7 @@ export const useAppStore = defineStore('app', {
|
|||
locale: true, // 多语言图标
|
||||
message: true, // 消息图标
|
||||
tagsView: true, // 标签页
|
||||
tagsViewImmerse: false, // 标签页沉浸
|
||||
tagsViewIcon: true, // 是否显示标签图标
|
||||
logo: true, // logo
|
||||
fixedHeader: true, // 固定toolheader
|
||||
|
@ -131,6 +133,9 @@ export const useAppStore = defineStore('app', {
|
|||
getTagsView(): boolean {
|
||||
return this.tagsView
|
||||
},
|
||||
getTagsViewImmerse(): boolean {
|
||||
return this.tagsViewImmerse
|
||||
},
|
||||
getTagsViewIcon(): boolean {
|
||||
return this.tagsViewIcon
|
||||
},
|
||||
|
@ -208,6 +213,9 @@ export const useAppStore = defineStore('app', {
|
|||
setTagsView(tagsView: boolean) {
|
||||
this.tagsView = tagsView
|
||||
},
|
||||
setTagsViewImmerse(tagsViewImmerse: boolean) {
|
||||
this.tagsViewImmerse = tagsViewImmerse
|
||||
},
|
||||
setTagsViewIcon(tagsViewIcon: boolean) {
|
||||
this.tagsViewIcon = tagsViewIcon
|
||||
},
|
||||
|
|
|
@ -40,10 +40,12 @@ export const usePermissionStore = defineStore('permission', {
|
|||
}
|
||||
const routerMap: AppRouteRecordRaw[] = generateRoute(res)
|
||||
// 动态路由,404一定要放到最后面
|
||||
// preschooler:vue-router@4以后已支持静态404路由,此处可不再追加
|
||||
this.addRouters = routerMap.concat([
|
||||
{
|
||||
path: '/:path(.*)*',
|
||||
redirect: '/404',
|
||||
// redirect: '/404',
|
||||
component: () => import('@/views/Error/404.vue'),
|
||||
name: '404Page',
|
||||
meta: {
|
||||
hidden: true,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@import './var.css';
|
||||
@import './FormCreate/index.scss';
|
||||
@import './theme.scss';
|
||||
@import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
|
||||
.reset-margin [class*='el-icon'] + span {
|
||||
|
|
|
@ -64,3 +64,11 @@ body {
|
|||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
*,
|
||||
:after,
|
||||
:before {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
|
|
@ -56,11 +56,11 @@ export const defaultShortcuts = [
|
|||
* 时间日期转换
|
||||
* @param date 当前时间,new Date() 格式
|
||||
* @param format 需要转换的时间格式字符串
|
||||
* @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
|
||||
* @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
|
||||
* @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
|
||||
* @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
|
||||
* @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
|
||||
* @description format 字符串随意,如 `YYYY-MM、YYYY-MM-DD`
|
||||
* @description format 季度:"YYYY-MM-DD HH:mm:ss QQQQ"
|
||||
* @description format 星期:"YYYY-MM-DD HH:mm:ss WWW"
|
||||
* @description format 几周:"YYYY-MM-DD HH:mm:ss ZZZ"
|
||||
* @description format 季度 + 星期 + 几周:"YYYY-MM-DD HH:mm:ss WWW QQQQ ZZZ"
|
||||
* @returns 返回拼接后的时间字符串
|
||||
*/
|
||||
export function formatDate(date: Date, format?: string): string {
|
||||
|
@ -110,7 +110,7 @@ export function getWeek(dateTime: Date): number {
|
|||
* @description param 3天: 60 * 60* 24 * 1000 * 3
|
||||
* @returns 返回拼接后的时间字符串
|
||||
*/
|
||||
export function formatPast(param: string | Date, format = 'YYYY-mm-dd HH:MM:SS'): string {
|
||||
export function formatPast(param: string | Date, format = 'YYYY-MM-DD HH:mm:ss'): string {
|
||||
// 传入格式处理、存储转换值
|
||||
let t: any, s: number
|
||||
// 获取js 时间戳
|
||||
|
|
|
@ -18,8 +18,8 @@ export const isObject = (val: any): val is Record<any, any> => {
|
|||
return val !== null && is(val, 'Object')
|
||||
}
|
||||
|
||||
export const isEmpty = <T = unknown>(val: T): val is T => {
|
||||
if (val === null) {
|
||||
export const isEmpty = (val: any): boolean => {
|
||||
if (val === null || val === undefined || typeof val === 'undefined') {
|
||||
return true
|
||||
}
|
||||
if (isArray(val) || isString(val)) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
>
|
||||
<div class="relative mx-auto h-full flex">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`"
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
|
||||
>
|
||||
<!-- 左上角的 logo + 系统标题 -->
|
||||
<div class="relative flex items-center text-white">
|
||||
|
@ -27,24 +27,27 @@
|
|||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px">
|
||||
<div
|
||||
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
|
||||
>
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
class="flex items-center justify-between at-2xl:justify-end at-xl:justify-end"
|
||||
style="color: var(--el-text-color-primary);"
|
||||
>
|
||||
<div class="flex items-center at-2xl:hidden at-xl:hidden">
|
||||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
<span class="text-20px font-bold" >{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-end space-x-10px">
|
||||
<div class="flex items-center justify-end space-x-10px h-48px">
|
||||
<ThemeSwitch />
|
||||
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
|
||||
<LocaleDropdown />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
class="m-auto h-full w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
>
|
||||
<!-- 账号登录 -->
|
||||
<LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
|
@ -102,3 +105,15 @@ $prefix-cls: #{$namespace}-login;
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.dark .login-form {
|
||||
.el-divider__text {
|
||||
background-color: var(--login-bg-color);
|
||||
}
|
||||
|
||||
.el-card {
|
||||
background-color: var(--login-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="relative h-[100%] lt-xl:bg-[var(--login-bg-color)] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px"
|
||||
class="relative h-[100%] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px lt-xl:px-10px"
|
||||
>
|
||||
<div class="relative mx-auto h-full flex">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`"
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
|
||||
>
|
||||
<!-- 左上角的 logo + 系统标题 -->
|
||||
<div class="relative flex items-center text-white">
|
||||
|
@ -27,7 +27,9 @@
|
|||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px">
|
||||
<div
|
||||
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
|
||||
>
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
|
@ -36,7 +38,7 @@
|
|||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-end space-x-10px">
|
||||
<div class="flex items-center justify-end space-x-10px h-48px">
|
||||
<ThemeSwitch />
|
||||
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
|
||||
</div>
|
||||
|
@ -44,7 +46,7 @@
|
|||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
class="m-auto h-full w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
>
|
||||
<!-- 账号登录 -->
|
||||
<el-form
|
||||
|
@ -112,9 +114,9 @@
|
|||
</el-checkbox>
|
||||
</el-col>
|
||||
<el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary">{{
|
||||
t('login.forgetPassword')
|
||||
}}</el-link>
|
||||
<el-link style="float: right" type="primary"
|
||||
>{{ t('login.forgetPassword') }}
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
|
@ -274,10 +276,11 @@ const handleLogin = async (params) => {
|
|||
const code = route?.query?.code as string
|
||||
const state = route?.query?.state as string
|
||||
|
||||
const loginDataLoginForm = { ...loginData.loginForm }
|
||||
const res = await LoginApi.login({
|
||||
// 账号密码登录
|
||||
username: loginData.loginForm.username,
|
||||
password: loginData.loginForm.password,
|
||||
username: loginDataLoginForm.username,
|
||||
password: loginDataLoginForm.password,
|
||||
captchaVerification: params.captchaVerification,
|
||||
// 社交登录
|
||||
socialCode: code,
|
||||
|
@ -292,8 +295,8 @@ const handleLogin = async (params) => {
|
|||
text: '正在加载系统中...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
if (loginData.loginForm.rememberMe) {
|
||||
authUtil.setLoginForm(loginData.loginForm)
|
||||
if (loginDataLoginForm.rememberMe) {
|
||||
authUtil.setLoginForm(loginDataLoginForm)
|
||||
} else {
|
||||
authUtil.removeLoginForm()
|
||||
}
|
||||
|
|
|
@ -249,8 +249,9 @@ const handleLogin = async (params) => {
|
|||
if (!data) {
|
||||
return
|
||||
}
|
||||
loginData.loginForm.captchaVerification = params.captchaVerification
|
||||
const res = await LoginApi.login(loginData.loginForm)
|
||||
const loginDataLoginForm = { ...loginData.loginForm }
|
||||
loginDataLoginForm.captchaVerification = params.captchaVerification
|
||||
const res = await LoginApi.login(loginDataLoginForm)
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
|
@ -259,8 +260,8 @@ const handleLogin = async (params) => {
|
|||
text: '正在加载系统中...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
if (loginData.loginForm.rememberMe) {
|
||||
authUtil.setLoginForm(loginData.loginForm)
|
||||
if (loginDataLoginForm.rememberMe) {
|
||||
authUtil.setLoginForm(loginDataLoginForm)
|
||||
} else {
|
||||
authUtil.removeLoginForm()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-row v-show="getShow" style="margin-right: -10px; margin-left: -10px">
|
||||
<el-row v-show="getShow" class="login-form" style="margin-right: -10px; margin-left: -10px">
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
</el-col>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
v-show="getShow"
|
||||
:rules="rules"
|
||||
:schema="schema"
|
||||
class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
|
||||
class="w-[100%] dark:(border-1 border-[var(--el-border-color)] border-solid)"
|
||||
hide-required-asterisk
|
||||
label-position="top"
|
||||
size="large"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<el-checkbox
|
||||
v-for="scope in queryParams.scopes"
|
||||
:key="scope"
|
||||
:label="scope"
|
||||
:value="scope"
|
||||
style="display: block; margin-bottom: -10px"
|
||||
>
|
||||
{{ formatScope(scope) }}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema">
|
||||
<template #sex="form">
|
||||
<el-radio-group v-model="form['sex']">
|
||||
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio>
|
||||
<el-radio :label="2">{{ t('profile.user.woman') }}</el-radio>
|
||||
<el-radio :value="1">{{ t('profile.user.man') }}</el-radio>
|
||||
<el-radio :value="2">{{ t('profile.user.woman') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</Form>
|
||||
|
@ -27,7 +27,7 @@ defineOptions({ name: 'BasicInfo' })
|
|||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage() // 消息弹窗
|
||||
const userStore = useUserStore()
|
||||
const userStore = useUserStore()
|
||||
// 表单校验
|
||||
const rules = reactive<FormRules>({
|
||||
nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }],
|
||||
|
|
|
@ -78,7 +78,7 @@ const userStore = useUserStore()
|
|||
const messageContainer: any = ref(null)
|
||||
const isScrolling = ref(false) //用于判断用户是否在滚动
|
||||
|
||||
const userAvatar = computed(() => userStore.user.avatar ?? userAvatarDefaultImg)
|
||||
const userAvatar = computed(() => userStore.user.avatar || userAvatarDefaultImg)
|
||||
const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg)
|
||||
|
||||
// 定义 props
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
|
||||
<span>思维导图预览</span>
|
||||
<!-- 展示在右上角 -->
|
||||
<el-button type="primary" v-show="isEnd" @click="downloadImage" size="small">
|
||||
<el-button v-show="isEnd" size="small" type="primary" @click="downloadImage">
|
||||
<template #icon>
|
||||
<Icon icon="ph:copy-bold" />
|
||||
</template>
|
||||
|
@ -20,14 +20,14 @@
|
|||
</div>
|
||||
|
||||
<div ref="mindMapRef" class="wh-full">
|
||||
<svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" />
|
||||
<svg ref="svgRef" :style="{ height: `${contentAreaHeight}px` }" class="w-full" />
|
||||
<div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { Markmap } from 'markmap-view'
|
||||
import { Transformer } from 'markmap-lib'
|
||||
import { Toolbar } from 'markmap-toolbar'
|
||||
|
@ -137,6 +137,7 @@ defineExpose({
|
|||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.my-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -149,13 +150,16 @@ defineExpose({
|
|||
@extend .hide-scroll-bar;
|
||||
}
|
||||
}
|
||||
|
||||
// markmap的tool样式覆盖
|
||||
:deep(.markmap) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.mm-toolbar-brand) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.mm-toolbar) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<!--表单区域-->
|
||||
<Left
|
||||
ref="leftRef"
|
||||
:is-generating="isGenerating"
|
||||
@submit="submit"
|
||||
@direct-generate="directGenerate"
|
||||
:is-generating="isGenerating"
|
||||
/>
|
||||
<!--右边生成思维导图区域-->
|
||||
<Right
|
||||
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import Left from './components/Left.vue'
|
||||
import Right from './components/Right.vue'
|
||||
import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
@ -69,7 +69,7 @@ import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
|
|||
import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
|
||||
import {FormRules} from "element-plus";
|
||||
import { FormRules } from 'element-plus'
|
||||
|
||||
/** AI 聊天角色 表单 */
|
||||
defineOptions({ name: 'ChatRoleForm' })
|
||||
|
|
|
@ -1,20 +1,14 @@
|
|||
<template>
|
||||
<ContentWrap class="w-300px h-full mb-[0!important]">
|
||||
<el-radio-group v-model="generateMode" class="mb-15px">
|
||||
<el-radio-button label="desc">
|
||||
描述模式
|
||||
</el-radio-button>
|
||||
<el-radio-button label="lyric">
|
||||
歌词模式
|
||||
</el-radio-button>
|
||||
<el-radio-button value="desc"> 描述模式 </el-radio-button>
|
||||
<el-radio-button value="lyric"> 歌词模式 </el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<!-- 描述模式/歌词模式 切换 -->
|
||||
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef"/>
|
||||
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef" />
|
||||
|
||||
<el-button type="primary" round class="w-full" @click="generateMusic">
|
||||
创作音乐
|
||||
</el-button>
|
||||
<el-button type="primary" round class="w-full" @click="generateMusic"> 创作音乐 </el-button>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
|
@ -34,8 +28,8 @@ const modeRef = ref<Nullable<{ formData: Recordable }>>(null)
|
|||
*@Description: 根据信息生成音乐
|
||||
*@MethodAuthor: xiaohong
|
||||
*@Date: 2024-06-27 16:40:16
|
||||
*/
|
||||
function generateMusic () {
|
||||
emits('generate-music', {formData: unref(modeRef)?.formData})
|
||||
*/
|
||||
function generateMusic() {
|
||||
emits('generate-music', { formData: unref(modeRef)?.formData })
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否发布" prop="publicStatus">
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<!-- 定义 tab 组件:撰写/回复等 -->
|
||||
<DefineTab v-slot="{ active, text, itemClick }">
|
||||
<span
|
||||
class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black"
|
||||
:class="active ? 'text-black shadow-md' : 'hover:bg-[#DDDFE3]'"
|
||||
class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black"
|
||||
@click="itemClick"
|
||||
>
|
||||
{{ text }}
|
||||
|
@ -14,9 +14,9 @@
|
|||
<h3 class="mt-5 mb-3 flex items-center justify-between text-[14px]">
|
||||
<span>{{ label }}</span>
|
||||
<span
|
||||
@click="hintClick"
|
||||
v-if="hint"
|
||||
class="flex items-center text-[12px] text-[#846af7] cursor-pointer select-none"
|
||||
@click="hintClick"
|
||||
>
|
||||
<Icon icon="ep:question-filled" />
|
||||
{{ hint }}
|
||||
|
@ -29,17 +29,17 @@
|
|||
<div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
|
||||
<div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
|
||||
<div
|
||||
class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
|
||||
:class="
|
||||
selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'
|
||||
"
|
||||
class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
|
||||
>
|
||||
<ReuseTab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
:text="tab.text"
|
||||
:active="tab.value === selectedTab"
|
||||
:itemClick="() => switchTab(tab.value)"
|
||||
:text="tab.text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -49,36 +49,36 @@
|
|||
>
|
||||
<div>
|
||||
<template v-if="selectedTab === 1">
|
||||
<ReuseLabel label="写作内容" hint="示例" :hint-click="() => example('write')" />
|
||||
<ReuseLabel :hint-click="() => example('write')" hint="示例" label="写作内容" />
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
:maxlength="500"
|
||||
v-model="formData.prompt"
|
||||
:maxlength="500"
|
||||
:rows="5"
|
||||
placeholder="请输入写作内容"
|
||||
showWordLimit
|
||||
type="textarea"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<ReuseLabel label="原文" hint="示例" :hint-click="() => example('reply')" />
|
||||
<ReuseLabel :hint-click="() => example('reply')" hint="示例" label="原文" />
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
:maxlength="500"
|
||||
v-model="formData.originalContent"
|
||||
:maxlength="500"
|
||||
:rows="5"
|
||||
placeholder="请输入原文"
|
||||
showWordLimit
|
||||
type="textarea"
|
||||
/>
|
||||
|
||||
<ReuseLabel label="回复内容" />
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
:maxlength="500"
|
||||
v-model="formData.prompt"
|
||||
:maxlength="500"
|
||||
:rows="5"
|
||||
placeholder="请输入回复内容"
|
||||
showWordLimit
|
||||
type="textarea"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
@ -93,18 +93,18 @@
|
|||
|
||||
<div class="flex items-center justify-center mt-3">
|
||||
<el-button :disabled="isWriting" @click="reset">重置</el-button>
|
||||
<el-button :loading="isWriting" @click="submit" color="#846af7">生成</el-button>
|
||||
<el-button :loading="isWriting" color="#846af7" @click="submit">生成</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { createReusableTemplate } from '@vueuse/core'
|
||||
import { ref } from 'vue'
|
||||
import Tag from './Tag.vue'
|
||||
import { WriteVO } from 'src/api/ai/write'
|
||||
import { WriteVO } from '@/api/ai/write'
|
||||
import { omit } from 'lodash-es'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants'
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
<template>
|
||||
<div
|
||||
class="h-50px position-fixed bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
|
||||
>
|
||||
<el-popover :visible="passVisible" placement="top-end" :width="500" trigger="click">
|
||||
<template #reference>
|
||||
<el-button plain type="success" @click="openPopover('1')">
|
||||
<Icon icon="ep:select" /> 通过
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
|
||||
<el-form
|
||||
label-position="top"
|
||||
class="mb-auto"
|
||||
ref="formRef"
|
||||
:model="auditForm"
|
||||
:rules="auditRule"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
|
||||
{{ processInstance?.startUser.nickname }}
|
||||
<el-tag size="small" type="info" class="ml-8px">
|
||||
{{ processInstance?.startUser.deptName }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
<el-card v-if="runningTask.formId > 0" class="mb-15px !-mt-10px">
|
||||
<template #header>
|
||||
<span class="el-icon-picture-outline"> 填写表单【{{ runningTask?.formName }}】 </span>
|
||||
</template>
|
||||
<form-create
|
||||
v-model="approveForm.value"
|
||||
v-model:api="approveFormFApi"
|
||||
:option="approveForm.option"
|
||||
:rule="approveForm.rule"
|
||||
/>
|
||||
</el-card>
|
||||
<el-form-item label="审批建议" prop="reason">
|
||||
<el-input v-model="auditForm.reason" placeholder="请输入审批建议" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送人" prop="copyUserIds">
|
||||
<el-select v-model="auditForm.copyUserIds" multiple placeholder="请选择抄送人">
|
||||
<el-option
|
||||
v-for="itemx in userOptions"
|
||||
:key="itemx.id"
|
||||
:label="itemx.nickname"
|
||||
:value="itemx.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button :disabled="formLoading" type="success" @click="handleAudit(true)">
|
||||
通过
|
||||
</el-button>
|
||||
<el-button @click="passVisible = false"> 取消 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover :visible="rejectVisible" placement="top-end" :width="500" trigger="click">
|
||||
<template #reference>
|
||||
<el-button class="mr-20px" plain type="danger" @click="openPopover('2')">
|
||||
<Icon icon="ep:close" /> 拒绝
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
|
||||
<el-form
|
||||
label-position="top"
|
||||
class="mb-auto"
|
||||
ref="formRef"
|
||||
:model="auditForm"
|
||||
:rules="auditRule"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
|
||||
{{ processInstance?.startUser.nickname }}
|
||||
<el-tag size="small" type="info" class="ml-8px">
|
||||
{{ processInstance?.startUser.deptName }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
<el-card v-if="runningTask.formId > 0" class="mb-15px !-mt-10px">
|
||||
<template #header>
|
||||
<span class="el-icon-picture-outline"> 填写表单【{{ runningTask?.formName }}】 </span>
|
||||
</template>
|
||||
<form-create
|
||||
v-model="approveForm.value"
|
||||
v-model:api="approveFormFApi"
|
||||
:option="approveForm.option"
|
||||
:rule="approveForm.rule"
|
||||
/>
|
||||
</el-card>
|
||||
<el-form-item label="审批建议" prop="reason">
|
||||
<el-input v-model="auditForm.reason" placeholder="请输入审批建议" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送人" prop="copyUserIds">
|
||||
<el-select v-model="auditForm.copyUserIds" multiple placeholder="请选择抄送人">
|
||||
<el-option
|
||||
v-for="itemx in userOptions"
|
||||
:key="itemx.id"
|
||||
:label="itemx.nickname"
|
||||
:value="itemx.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button :disabled="formLoading" type="danger" @click="handleAudit(false)">
|
||||
拒绝
|
||||
</el-button>
|
||||
<el-button @click="rejectVisible = false"> 取消 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div @click="handleSend"> <Icon :size="14" icon="svg-icon:send" /> 抄送 </div>
|
||||
<div @click="openTaskUpdateAssigneeForm">
|
||||
<Icon :size="14" icon="fa:share-square-o" /> 转交
|
||||
</div>
|
||||
<div @click="handleDelegate"> <Icon :size="14" icon="ep:position" /> 委派 </div>
|
||||
<div @click="handleSign"> <Icon :size="14" icon="ep:plus" /> 加签 </div>
|
||||
<div @click="handleBack"> <Icon :size="14" icon="fa:mail-reply" /> 退回 </div>
|
||||
</div>
|
||||
<!-- 弹窗:转派审批人 -->
|
||||
<TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
|
||||
<!-- 弹窗:回退节点 -->
|
||||
<TaskReturnForm ref="taskReturnFormRef" @success="getDetail" />
|
||||
<!-- 弹窗:委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
||||
<TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
|
||||
<!-- 弹窗:加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
|
||||
<TaskSignCreateForm ref="taskSignCreateFormRef" @success="getDetail" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import TaskReturnForm from './dialog/TaskReturnForm.vue'
|
||||
import TaskDelegateForm from './dialog/TaskDelegateForm.vue'
|
||||
import TaskTransferForm from './dialog/TaskTransferForm.vue'
|
||||
import TaskSignCreateForm from './dialog/TaskSignCreateForm.vue'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
defineOptions({ name: 'ProcessInstanceBtnConatiner' })
|
||||
|
||||
const userId = useUserStore().getUser.id // 当前登录的编号
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
defineProps({
|
||||
processInstance: propTypes.any, // 流程实例信息
|
||||
userOptions: propTypes.any
|
||||
})
|
||||
const formLoading = ref(false) // 表单加载中
|
||||
const passVisible = ref(false) // 是否显示
|
||||
const rejectVisible = ref(false) // 是否显示
|
||||
// ========== 审批信息 ==========
|
||||
const runningTask = ref<any>({}) // 运行中的任务
|
||||
const auditForm = ref<any>({}) // 审批任务的表单
|
||||
const approveForm = ref<any>({}) // 审批通过时,额外的补充信息
|
||||
const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
|
||||
const formRef = ref()
|
||||
const auditRule = reactive({
|
||||
reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
|
||||
watch(
|
||||
() => approveFormFApi.value,
|
||||
(val) => {
|
||||
val?.btn?.show(false)
|
||||
val?.resetBtn?.show(false)
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
/**
|
||||
* 设置 runningTasks 中的任务
|
||||
*/
|
||||
const loadRunningTask = (tasks) => {
|
||||
runningTask.value = {}
|
||||
auditForm.value = {}
|
||||
approveForm.value = {}
|
||||
approveFormFApi.value = {}
|
||||
tasks.forEach((task) => {
|
||||
if (!isEmpty(task.children)) {
|
||||
loadRunningTask(task.children)
|
||||
}
|
||||
// 2.1 只有待处理才需要
|
||||
if (task.status !== 1 && task.status !== 6) {
|
||||
return
|
||||
}
|
||||
// 2.2 自己不是处理人
|
||||
if (!task.assigneeUser || task.assigneeUser.id !== userId) {
|
||||
return
|
||||
}
|
||||
// 2.3 添加到处理任务
|
||||
runningTask.value = { ...task }
|
||||
auditForm.value = {
|
||||
reason: '',
|
||||
copyUserIds: []
|
||||
}
|
||||
|
||||
// 2.4 处理 approve 表单
|
||||
if (task.formId && task.formConf) {
|
||||
const tempApproveForm = {}
|
||||
setConfAndFields2(tempApproveForm, task.formConf, task.formFields, task.formVariables)
|
||||
approveForm.value = tempApproveForm
|
||||
} else {
|
||||
approveForm.value = {} // 占位,避免为空
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 处理审批通过和不通过的操作 */
|
||||
const handleAudit = async (pass) => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const auditFormRef = proxy.$refs['formRef']
|
||||
// 1.2 校验表单
|
||||
const elForm = unref(auditFormRef)
|
||||
if (!elForm) return
|
||||
const valid = await elForm.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 2.1 提交审批
|
||||
const data = {
|
||||
id: runningTask.value.id,
|
||||
reason: auditForm.value.reason,
|
||||
copyUserIds: auditForm.value.copyUserIds
|
||||
}
|
||||
if (pass) {
|
||||
// 审批通过,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
||||
const formCreateApi = approveFormFApi.value
|
||||
if (Object.keys(formCreateApi)?.length > 0) {
|
||||
await formCreateApi.validate()
|
||||
// @ts-ignore
|
||||
data.variables = approveForm.value.value
|
||||
}
|
||||
await TaskApi.approveTask(data)
|
||||
message.success('审批通过成功')
|
||||
} else {
|
||||
await TaskApi.rejectTask(data)
|
||||
message.success('审批不通过成功')
|
||||
}
|
||||
// 2.2 加载最新数据
|
||||
getDetail()
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/* 抄送 TODO */
|
||||
const handleSend = () => {}
|
||||
|
||||
const openPopover = (flag) => {
|
||||
passVisible.value = false
|
||||
rejectVisible.value = false
|
||||
formRef.value.resetFields()
|
||||
flag === '1' ? (passVisible.value = true) : (rejectVisible.value = true)
|
||||
}
|
||||
|
||||
/** 转派审批人 */
|
||||
const taskTransferFormRef = ref()
|
||||
const openTaskUpdateAssigneeForm = () => {
|
||||
taskTransferFormRef.value.open(runningTask.value.id)
|
||||
}
|
||||
|
||||
/** 处理审批退回的操作 */
|
||||
const taskDelegateForm = ref()
|
||||
const handleDelegate = async () => {
|
||||
taskDelegateForm.value.open(runningTask.value.id)
|
||||
}
|
||||
|
||||
/** 处理审批退回的操作 */
|
||||
const taskReturnFormRef = ref()
|
||||
const handleBack = async () => {
|
||||
taskReturnFormRef.value.open(runningTask.value.id)
|
||||
}
|
||||
|
||||
/** 处理审批加签的操作 */
|
||||
const taskSignCreateFormRef = ref()
|
||||
const handleSign = async () => {
|
||||
taskSignCreateFormRef.value.open(runningTask.value.id)
|
||||
}
|
||||
/** 获得详情 */
|
||||
const getDetail = () => {
|
||||
emit('success')
|
||||
}
|
||||
|
||||
defineExpose({ loadRunningTask })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-affix--fixed) {
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
> div {
|
||||
margin: 0 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:hover {
|
||||
color: #6db5ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,161 @@
|
|||
<template>
|
||||
<el-timeline class="pt-20px">
|
||||
<el-timeline-item v-for="(activity, index) in mockData" :key="index" size="large">
|
||||
<div class="flex flex-col items-start">
|
||||
<div class="font-bold"> {{ activity.name }}</div>
|
||||
<div class="color-#a1a6ae text-12px mb-10px"> {{ activity.assigneeUser.nickname }}</div>
|
||||
<div v-if="activity.opinion" class="text-#a5a5a5 text-12px w-100%">
|
||||
<div class="mb-5px">审批意见:</div>
|
||||
<div
|
||||
class="w-100% border-1px border-#a5a5a5 border-dashed rounded py-5px px-15px text-#2d2d2d"
|
||||
>
|
||||
{{ activity.opinion }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="activity.createTime" class="text-#a5a5a5 text-13px">
|
||||
{{ formatDate(activity.createTime) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 该节点用户的头像 -->
|
||||
<template #dot>
|
||||
<div class="w-35px h-35px position-relative">
|
||||
<img
|
||||
src="@/assets/imgs/avatar.jpg"
|
||||
class="rounded-full w-full h-full position-absolute bottom-6px right-12px"
|
||||
alt=""
|
||||
/>
|
||||
<div
|
||||
class="position-absolute top-16px left-8px bg-#fff rounded-full flex items-center content-center p-2px"
|
||||
>
|
||||
<Icon
|
||||
:size="12"
|
||||
:icon="optIconMap[activity.status]?.icon"
|
||||
:color="optIconMap[activity.status]?.color"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceTimeline' })
|
||||
defineProps({
|
||||
tasks: propTypes.array // 流程任务的数组
|
||||
})
|
||||
|
||||
const optIconMap = {
|
||||
// 审批中
|
||||
'1': {
|
||||
color: '#00b32a',
|
||||
icon: 'fa-solid:clock'
|
||||
},
|
||||
// 审批通过
|
||||
'2': { color: '#00b32a', icon: 'fa-solid:check-circle' },
|
||||
// 审批不通过
|
||||
'3': { color: '#f46b6c', icon: 'fa-solid:times-circle' }
|
||||
}
|
||||
|
||||
const mockData: any = [
|
||||
{
|
||||
id: 'fe1190ee-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: '发起人',
|
||||
createTime: 1725237646192,
|
||||
endTime: null,
|
||||
durationInMillis: null,
|
||||
status: 1,
|
||||
reason: null,
|
||||
ownerUser: null,
|
||||
assigneeUser: {
|
||||
id: 104,
|
||||
nickname: '测试号',
|
||||
deptId: 107,
|
||||
deptName: '运维部门'
|
||||
},
|
||||
taskDefinitionKey: 'task-01',
|
||||
processInstanceId: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
processInstance: {
|
||||
id: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: 'oa_leave',
|
||||
createTime: null,
|
||||
processDefinitionId: 'oa_leave:1:6e5ac269-5f87-11ef-bdb6-00a6181404fd',
|
||||
startUser: null
|
||||
},
|
||||
parentTaskId: null,
|
||||
children: null,
|
||||
formId: null,
|
||||
formName: null,
|
||||
formConf: null,
|
||||
formFields: null,
|
||||
formVariables: null
|
||||
},
|
||||
{
|
||||
id: 'fe1190ee-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: '领导审批',
|
||||
createTime: 1725237646192,
|
||||
endTime: null,
|
||||
durationInMillis: null,
|
||||
status: 2,
|
||||
reason: null,
|
||||
ownerUser: null,
|
||||
assigneeUser: {
|
||||
id: 104,
|
||||
nickname: '领导',
|
||||
deptId: 107,
|
||||
deptName: '运维部门'
|
||||
},
|
||||
taskDefinitionKey: 'task-01',
|
||||
processInstanceId: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
processInstance: {
|
||||
id: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: 'oa_leave',
|
||||
createTime: null,
|
||||
processDefinitionId: 'oa_leave:1:6e5ac269-5f87-11ef-bdb6-00a6181404fd',
|
||||
startUser: null
|
||||
},
|
||||
parentTaskId: null,
|
||||
children: null,
|
||||
formId: null,
|
||||
formName: null,
|
||||
formConf: null,
|
||||
formFields: null,
|
||||
formVariables: null
|
||||
},
|
||||
{
|
||||
id: 'fe1190ee-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: '财务总监审核',
|
||||
createTime: 1725237646192,
|
||||
endTime: null,
|
||||
durationInMillis: null,
|
||||
status: 3,
|
||||
reason: null,
|
||||
ownerUser: null,
|
||||
assigneeUser: {
|
||||
id: 104,
|
||||
nickname: '财务总监',
|
||||
deptId: 107,
|
||||
deptName: '运维部门'
|
||||
},
|
||||
taskDefinitionKey: 'task-01',
|
||||
processInstanceId: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
processInstance: {
|
||||
id: 'fe0c60c6-68c3-11ef-9c7d-00a6181404fd',
|
||||
name: 'oa_leave',
|
||||
createTime: null,
|
||||
processDefinitionId: 'oa_leave:1:6e5ac269-5f87-11ef-bdb6-00a6181404fd',
|
||||
startUser: null
|
||||
},
|
||||
parentTaskId: null,
|
||||
children: null,
|
||||
formId: null,
|
||||
formName: null,
|
||||
formConf: null,
|
||||
formFields: null,
|
||||
formVariables: null
|
||||
}
|
||||
]
|
||||
</script>
|
|
@ -9,7 +9,7 @@
|
|||
>
|
||||
<el-form-item label="减签任务" prop="id">
|
||||
<el-radio-group v-model="formData.id">
|
||||
<el-radio-button v-for="item in childrenTaskList" :key="item.id" :label="item.id">
|
||||
<el-radio-button v-for="item in childrenTaskList" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
({{ item.assigneeUser?.deptName || item.ownerUser?.deptName }} -
|
||||
{{ item.assigneeUser?.nickname || item.ownerUser?.nickname }})
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
<template>
|
||||
<ContentWrap :bodyStyle="{ padding: '10px 20px' }" class="position-relative">
|
||||
<img
|
||||
class="position-absolute right-20px"
|
||||
width="150"
|
||||
:src="auditIcons[processInstance.status]"
|
||||
alt=""
|
||||
/>
|
||||
<div class="text-#878c93">编号:{{ id }}</div>
|
||||
<el-divider class="!my-8px" />
|
||||
<div class="flex items-center gap-5 mb-10px">
|
||||
<div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="processInstance.status" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-5 mb-10px text-13px">
|
||||
<div class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600">
|
||||
<img class="rounded-full h-28px" src="@/assets/imgs/avatar.jpg" alt="" />
|
||||
{{ processInstance?.startUser?.nickname }}
|
||||
</div>
|
||||
<div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
|
||||
</div>
|
||||
|
||||
<el-tabs>
|
||||
<!-- 表单信息 -->
|
||||
<el-tab-pane label="表单信息">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="18" class="!flex !flex-col formCol">
|
||||
<!-- 表单信息 -->
|
||||
<div v-loading="processInstanceLoading" class="form-box flex flex-col mb-30px flex-1">
|
||||
<!-- 情况一:流程表单 -->
|
||||
<el-col
|
||||
v-if="processInstance?.processDefinition?.formType === 10"
|
||||
:offset="6"
|
||||
:span="16"
|
||||
>
|
||||
<form-create
|
||||
v-model="detailForm.value"
|
||||
v-model:api="fApi"
|
||||
:option="detailForm.option"
|
||||
:rule="detailForm.rule"
|
||||
/>
|
||||
</el-col>
|
||||
<!-- 情况二:业务表单 -->
|
||||
<div v-if="processInstance?.processDefinition?.formType === 20">
|
||||
<BusinessFormComponent :id="processInstance.businessKey" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏按钮 -->
|
||||
<ProcessInstanceOperationButton
|
||||
ref="operationButtonRef"
|
||||
:processInstance="processInstance"
|
||||
:userOptions="userOptions"
|
||||
@success="getDetail"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<!-- 审批记录时间线 -->
|
||||
<ProcessInstanceTimeline :process-instance="processInstance" :tasks="tasks" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- 流程图 -->
|
||||
<el-tab-pane label="流程图">
|
||||
<ProcessInstanceBpmnViewer
|
||||
:id="`${id}`"
|
||||
:bpmn-xml="bpmnXml"
|
||||
:loading="processInstanceLoading"
|
||||
:process-instance="processInstance"
|
||||
:tasks="tasks"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<!-- 流转记录 -->
|
||||
<el-tab-pane label="流转记录">
|
||||
<ProcessInstanceTaskList
|
||||
:loading="tasksLoad"
|
||||
:process-instance="processInstance"
|
||||
:tasks="tasks"
|
||||
@refresh="getTaskList"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<!-- 流转评论 -->
|
||||
<el-tab-pane label="流转评论"> 流转评论 </el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
||||
import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
|
||||
import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
|
||||
import { registerComponent } from '@/utils/routerHelper'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import audit1 from '@/assets/svgs/bpm/audit1.svg'
|
||||
import audit2 from '@/assets/svgs/bpm/audit2.svg'
|
||||
import audit3 from '@/assets/svgs/bpm/audit3.svg'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
||||
|
||||
const { query } = useRoute() // 查询参数
|
||||
const message = useMessage() // 消息弹窗
|
||||
const id = query.id as unknown as string // 流程实例的编号
|
||||
const processInstanceLoading = ref(false) // 流程实例的加载中
|
||||
const processInstance = ref<any>({}) // 流程实例
|
||||
const operationButtonRef = ref()
|
||||
const bpmnXml = ref('') // BPMN XML
|
||||
const tasksLoad = ref(true) // 任务的加载中
|
||||
const tasks = ref<any[]>([]) // 任务列表
|
||||
const auditIcons = {
|
||||
1: audit1,
|
||||
2: audit2,
|
||||
3: audit3
|
||||
}
|
||||
|
||||
// ========== 申请信息 ==========
|
||||
const fApi = ref<ApiAttrs>() //
|
||||
const detailForm = ref({
|
||||
rule: [],
|
||||
option: {},
|
||||
value: {}
|
||||
}) // 流程实例的表单详情
|
||||
|
||||
/** 获得详情 */
|
||||
const getDetail = () => {
|
||||
// 1. 获得流程实例相关
|
||||
getProcessInstance()
|
||||
// 2. 获得流程任务列表(审批记录)
|
||||
getTaskList()
|
||||
}
|
||||
|
||||
/** 加载流程实例 */
|
||||
const BusinessFormComponent = ref<any>(null) // 异步组件
|
||||
const getProcessInstance = async () => {
|
||||
try {
|
||||
processInstanceLoading.value = true
|
||||
const data = await ProcessInstanceApi.getProcessInstance(id)
|
||||
if (!data) {
|
||||
message.error('查询不到流程信息!')
|
||||
return
|
||||
}
|
||||
processInstance.value = data
|
||||
|
||||
// 设置表单信息
|
||||
const processDefinition = data.processDefinition
|
||||
if (processDefinition.formType === 10) {
|
||||
setConfAndFields2(
|
||||
detailForm,
|
||||
processDefinition.formConf,
|
||||
processDefinition.formFields,
|
||||
data.formVariables
|
||||
)
|
||||
nextTick().then(() => {
|
||||
fApi.value?.btn.show(false)
|
||||
fApi.value?.resetBtn.show(false)
|
||||
fApi.value?.disabled(true)
|
||||
})
|
||||
} else {
|
||||
// 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
|
||||
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
|
||||
}
|
||||
|
||||
// 加载流程图
|
||||
bpmnXml.value = (await DefinitionApi.getProcessDefinition(processDefinition.id))?.bpmnXml
|
||||
} finally {
|
||||
processInstanceLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载任务列表 */
|
||||
const getTaskList = async () => {
|
||||
try {
|
||||
// 获得未取消的任务
|
||||
tasksLoad.value = true
|
||||
const data = await TaskApi.getTaskListByProcessInstanceId(id)
|
||||
tasks.value = []
|
||||
// 1.1 移除已取消的审批
|
||||
data.forEach((task) => {
|
||||
if (task.status !== 4) {
|
||||
tasks.value.push(task)
|
||||
}
|
||||
})
|
||||
// 1.2 排序,将未完成的排在前面,已完成的排在后面;
|
||||
tasks.value.sort((a, b) => {
|
||||
// 有已完成的情况,按照完成时间倒序
|
||||
if (a.endTime && b.endTime) {
|
||||
return b.endTime - a.endTime
|
||||
} else if (a.endTime) {
|
||||
return 1
|
||||
} else if (b.endTime) {
|
||||
return -1
|
||||
// 都是未完成,按照创建时间倒序
|
||||
} else {
|
||||
return b.createTime - a.createTime
|
||||
}
|
||||
})
|
||||
|
||||
// 获得需要自己审批的任务
|
||||
operationButtonRef.value?.loadRunningTask(tasks.value)
|
||||
} finally {
|
||||
tasksLoad.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
onMounted(async () => {
|
||||
getDetail()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-box {
|
||||
:deep(.el-card) {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -76,7 +76,7 @@
|
|||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue