Merge branch 'master' of https://gitee.com/zhijia3278/yudao-ui-admin-vue3
commit
b0da657781
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "yudao-ui-admin-vue3",
|
||||
"version": "2.6.0-snapshot",
|
||||
"version": "2.6.1-snapshot",
|
||||
"description": "基于vue3、vite4、element-plus、typesScript",
|
||||
"author": "xingyu",
|
||||
"private": false,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export type ApprovalNodeInfo = {
|
|||
status: number
|
||||
startTime?: Date
|
||||
endTime?: Date
|
||||
processInstanceId?: string
|
||||
candidateUsers?: User[]
|
||||
tasks: ApprovalTaskInfo[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export interface SocialUserVO {
|
|||
}
|
||||
|
||||
// 查询社交用户列表
|
||||
export const getSocialUserPage = async (params) => {
|
||||
export const getSocialUserPage = async (params: any) => {
|
||||
return await request.get({ url: `/system/social-user/page`, params })
|
||||
}
|
||||
|
||||
|
|
@ -22,3 +22,8 @@ export const getSocialUserPage = async (params) => {
|
|||
export const getSocialUser = async (id: number) => {
|
||||
return await request.get({ url: `/system/social-user/get?id=` + id })
|
||||
}
|
||||
|
||||
// 获得绑定社交用户列表
|
||||
export const getBindSocialUserList = async () => {
|
||||
return await request.get({ url: '/system/social-user/get-bind-list' })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ export interface ProfileVO {
|
|||
id: number
|
||||
name: string
|
||||
}[]
|
||||
socialUsers: {
|
||||
type: number
|
||||
openid: string
|
||||
}[]
|
||||
email: string
|
||||
mobile: string
|
||||
sex: number
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ const activeAppLink = ref({} as AppLink)
|
|||
/** 打开弹窗 */
|
||||
const dialogVisible = ref(false)
|
||||
const open = (link: string) => {
|
||||
activeAppLink.value.path = link
|
||||
// 进入页面时先重置 activeAppLink
|
||||
activeAppLink.value = { name: '', path: '' }
|
||||
dialogVisible.value = true
|
||||
|
||||
// 滚动到当前的链接
|
||||
|
|
@ -102,8 +103,11 @@ defineExpose({ open })
|
|||
|
||||
// 处理 APP 链接选中
|
||||
const handleAppLinkSelected = (appLink: AppLink) => {
|
||||
// 只有不同链接时才更新(避免重复触发)
|
||||
if (!isSameLink(appLink.path, activeAppLink.value.path)) {
|
||||
activeAppLink.value = appLink
|
||||
// 如果新链接的 path 为空,则沿用当前 activeAppLink 的 path
|
||||
const path = appLink.path || activeAppLink.value.path
|
||||
activeAppLink.value = { ...appLink, path: path }
|
||||
}
|
||||
switch (appLink.type) {
|
||||
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
|
||||
|
|
@ -170,7 +174,7 @@ const groupBtnRefs = ref<ButtonInstance[]>([])
|
|||
const scrollToGroupBtn = (group: string) => {
|
||||
const groupBtn = groupBtnRefs.value
|
||||
.map((btn: ButtonInstance) => btn['ref'])
|
||||
.find((ref: Node) => ref.textContent === group)
|
||||
.find((ref: HTMLButtonElement) => ref.textContent === group)
|
||||
if (groupBtn) {
|
||||
groupScrollbar.value?.setScrollTop(groupBtn.offsetTop)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,6 @@ function openModal() {
|
|||
}
|
||||
|
||||
function closeModal() {
|
||||
debugger
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<template v-for="(cell, cellIndex) in cellList" :key="cellIndex">
|
||||
<template v-if="selectedHotAreaIndex === cellIndex">
|
||||
<el-form-item :prop="`cell[${cellIndex}].type`" label="类型">
|
||||
<el-radio-group v-model="cell.type">
|
||||
<el-radio-group v-model="cell.type" @change="handleHotAreaSelected(cell, cellIndex)">
|
||||
<el-radio value="text">文字</el-radio>
|
||||
<el-radio value="image">图片</el-radio>
|
||||
<el-radio value="search">搜索框</el-radio>
|
||||
|
|
@ -44,9 +44,32 @@
|
|||
</template>
|
||||
<!-- 3. 搜索框 -->
|
||||
<template v-else>
|
||||
<el-form-item label="框体颜色" prop="backgroundColor">
|
||||
<ColorInput v-model="cell.backgroundColor" />
|
||||
</el-form-item>
|
||||
<el-form-item class="lef" label="文本颜色" prop="textColor">
|
||||
<ColorInput v-model="cell.textColor" />
|
||||
</el-form-item>
|
||||
<el-form-item :prop="`cell[${cellIndex}].placeholder`" label="提示文字">
|
||||
<el-input v-model="cell.placeholder" maxlength="10" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="文本位置" prop="placeholderPosition">
|
||||
<el-radio-group v-model="cell!.placeholderPosition">
|
||||
<el-tooltip content="居左" placement="top">
|
||||
<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 value="center">
|
||||
<Icon icon="ant-design:align-center-outlined" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="扫一扫" prop="showScan">
|
||||
<el-switch v-model="cell!.showScan" />
|
||||
</el-form-item>
|
||||
<el-form-item :prop="`cell[${cellIndex}].borderRadius`" label="圆角">
|
||||
<el-slider
|
||||
v-model="cell.borderRadius"
|
||||
|
|
@ -88,10 +111,17 @@ const cellCount = computed(() => (props.isMp ? 6 : 8))
|
|||
const selectedHotAreaIndex = ref(0)
|
||||
const handleHotAreaSelected = (cellValue: NavigationBarCellProperty, index: number) => {
|
||||
selectedHotAreaIndex.value = index
|
||||
// 默认设置为选中文字,并设置属性
|
||||
if (!cellValue.type) {
|
||||
cellValue.type = 'text'
|
||||
cellValue.textColor = '#111111'
|
||||
}
|
||||
// 如果点击的是搜索框,则初始化搜索框的属性
|
||||
if (cellValue.type === 'search') {
|
||||
cellValue.placeholderPosition = 'left'
|
||||
cellValue.backgroundColor = '#EEEEEE'
|
||||
cellValue.textColor = '#969799'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,14 @@ export interface NavigationBarCellProperty {
|
|||
imgUrl: string
|
||||
// 图片链接
|
||||
url: string
|
||||
// 搜索框:框体颜色
|
||||
backgroundColor: string
|
||||
// 搜索框:提示文字
|
||||
placeholder: string
|
||||
// 搜索框:提示文字位置
|
||||
placeholderPosition: string
|
||||
// 搜索框:是否显示扫一扫
|
||||
showScan: boolean
|
||||
// 搜索框:边框圆角半径
|
||||
borderRadius: number
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,9 +54,12 @@ const getCellStyle = (cell: NavigationBarCellProperty) => {
|
|||
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
|
||||
return {
|
||||
height: 30,
|
||||
showScan: false,
|
||||
backgroundColor: cell.backgroundColor,
|
||||
showScan: cell.showScan,
|
||||
placeholder: cell.placeholder,
|
||||
borderRadius: cell.borderRadius
|
||||
borderRadius: cell.borderRadius,
|
||||
textColor: cell.textColor,
|
||||
placeholderPosition: cell.placeholderPosition
|
||||
} as SearchProperty
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -269,6 +269,11 @@ watch(
|
|||
if (!val || selectedComponentIndex.value === -1) {
|
||||
return
|
||||
}
|
||||
// 如果是基础设置页,默认选中的索引改成 -1,为了防止删除组件后切换到此页导致报错
|
||||
// https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/792
|
||||
if (props.showTabBar) {
|
||||
selectedComponentIndex.value = -1
|
||||
}
|
||||
pageComponents.value[selectedComponentIndex.value] = selectedComponent.value!
|
||||
},
|
||||
{ deep: true }
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ watch(
|
|||
(options) => {
|
||||
if (echartRef) {
|
||||
echartRef?.setOption(options)
|
||||
echartRef?.resize()
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@
|
|||
<script setup lang="ts">
|
||||
import SimpleProcessModel from './SimpleProcessModel.vue'
|
||||
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
|
||||
import { getModel } from '@/api/bpm/model'
|
||||
import { getForm, FormVO } from '@/api/bpm/form'
|
||||
import { getForm } from '@/api/bpm/form'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
|
|
@ -43,18 +42,22 @@ defineOptions({
|
|||
const emits = defineEmits(['success']) // 保存成功事件
|
||||
|
||||
const props = defineProps({
|
||||
modelId: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
modelKey: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
modelName: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
// 流程表单 ID
|
||||
modelFormId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
// 表单类型
|
||||
modelFormType: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: BpmModelFormType.NORMAL,
|
||||
},
|
||||
// 可发起流程的人员编号
|
||||
startUserIds: {
|
||||
type: Array,
|
||||
|
|
@ -70,7 +73,31 @@ const props = defineProps({
|
|||
const processData = inject('processData') as Ref
|
||||
const loading = ref(false)
|
||||
const formFields = ref<string[]>([])
|
||||
const formType = ref(20)
|
||||
const formType = ref(props.modelFormType);
|
||||
|
||||
// 监听 modelFormType 变化
|
||||
watch(
|
||||
() => props.modelFormType,
|
||||
(newVal) => {
|
||||
formType.value = newVal;
|
||||
},
|
||||
);
|
||||
|
||||
// 监听 modelFormId 变化
|
||||
watch(
|
||||
() => props.modelFormId,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
const form = await getForm(newVal);
|
||||
formFields.value = form?.fields;
|
||||
} else {
|
||||
// 如果 modelFormId 为空,清空表单字段
|
||||
formFields.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
|
|
@ -90,6 +117,8 @@ provide('startUserIds', props.startUserIds)
|
|||
provide('startDeptIds', props.startDeptIds)
|
||||
provide('tasks', [])
|
||||
provide('processInstance', {})
|
||||
|
||||
|
||||
const message = useMessage() // 国际化
|
||||
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
||||
provide('processNodeTree', processNodeTree)
|
||||
|
|
@ -169,17 +198,17 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
|
|||
onMounted(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
// 获取表单字段
|
||||
if (props.modelId) {
|
||||
const bpmnModel = await getModel(props.modelId)
|
||||
if (bpmnModel) {
|
||||
formType.value = bpmnModel.formType
|
||||
if (formType.value === BpmModelFormType.NORMAL && bpmnModel.formId) {
|
||||
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
|
||||
formFields.value = bpmnForm?.fields
|
||||
}
|
||||
}
|
||||
}
|
||||
// // 获取表单字段
|
||||
// if (props.modelId) {
|
||||
// const bpmnModel = await getModel(props.modelId)
|
||||
// if (bpmnModel) {
|
||||
// formType.value = bpmnModel.formType
|
||||
// if (formType.value === BpmModelFormType.NORMAL && bpmnModel.formId) {
|
||||
// const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
|
||||
// formFields.value = bpmnForm?.fields
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 获得角色列表
|
||||
roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||
// 获得岗位列表
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<el-form-item label-position="top" label="请求头">
|
||||
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
|
||||
<div class="flex pb-4" v-for="(item, index) in props.header" :key="index">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.header.${index}.key`"
|
||||
|
|
@ -10,18 +10,20 @@
|
|||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input class="w-160px" v-model="item.key" />
|
||||
<el-input v-model="item.key" style="width: 160px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select class="w-100px!" v-model="item.type">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-form-item>
|
||||
<el-select v-model="item.type" style="width: 160px" @change="handleTypeChange(item)">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
|
|
@ -34,8 +36,8 @@
|
|||
>
|
||||
<el-input
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
class="w-160px"
|
||||
v-model="item.value"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
|
|
@ -48,8 +50,8 @@
|
|||
>
|
||||
<el-select
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
class="w-160px!"
|
||||
v-model="item.value"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
|
|
@ -70,7 +72,7 @@
|
|||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label-position="top" label="请求体">
|
||||
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
|
||||
<div class="flex pb-4" v-for="(item, index) in props.body" :key="index">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.body.${index}.key`"
|
||||
|
|
@ -80,18 +82,20 @@
|
|||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input class="w-160px" v-model="item.key" />
|
||||
<el-input v-model="item.key" style="width: 160px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select class="w-100px!" v-model="item.type">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-form-item>
|
||||
<el-select v-model="item.type" style="width: 160px" @change="handleTypeChange(item)">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
|
|
@ -104,8 +108,8 @@
|
|||
>
|
||||
<el-input
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
class="w-160px"
|
||||
v-model="item.value"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
|
|
@ -118,8 +122,8 @@
|
|||
>
|
||||
<el-select
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
class="w-160px!"
|
||||
v-model="item.value"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
|
|
@ -170,6 +174,13 @@ const props = defineProps({
|
|||
|
||||
// 流程表单字段,发起人字段
|
||||
const formFieldOptions = useFormFieldsAndStartUser()
|
||||
|
||||
/** 监听类型变化,清空值 */
|
||||
const handleTypeChange = (item: HttpRequestParam) => {
|
||||
// 当类型改变时,清空值
|
||||
item.value = ''
|
||||
}
|
||||
|
||||
/** 添加请求配置项 */
|
||||
const addHttpRequestParam = (arr: HttpRequestParam[]) => {
|
||||
arr.push({
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="flex pt-2" v-for="(item, index) in setting.response" :key="index">
|
||||
<div class="flex pt-4" v-for="(item, index) in setting.response" :key="index">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${formItemPrefix}.response.${index}.key`"
|
||||
|
|
@ -74,10 +74,12 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<div class="pt-1">
|
||||
<el-button type="primary" text @click="addHttpResponseSetting(setting.response!)">
|
||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,16 @@
|
|||
"name": "fields",
|
||||
"type": "Field",
|
||||
"isMany": true
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "String",
|
||||
"isAttr": true
|
||||
},
|
||||
{
|
||||
"name": "eventDefinitions",
|
||||
"type": "bpmn:TimerEventDefinition",
|
||||
"isMany": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@
|
|||
v-model="timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
@change="() => updateTimeModdle()"
|
||||
@change="
|
||||
() => {
|
||||
updateTimeModdle()
|
||||
updateElementExtensions()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-select
|
||||
|
|
@ -55,7 +60,12 @@
|
|||
v-model="maxRemindCount"
|
||||
:min="1"
|
||||
:max="10"
|
||||
@change="() => updateTimeModdle()"
|
||||
@change="
|
||||
() => {
|
||||
updateTimeModdle()
|
||||
updateElementExtensions()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
|
@ -65,7 +75,7 @@
|
|||
import {
|
||||
TimeUnitType,
|
||||
TIME_UNIT_TYPES,
|
||||
TIMEOUT_HANDLER_TYPES,
|
||||
TIMEOUT_HANDLER_TYPES
|
||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||
import { convertTimeUnit } from '@/components/SimpleProcessDesignerV2/src/utils'
|
||||
|
||||
|
|
@ -195,6 +205,7 @@ const onTimeUnitChange = () => {
|
|||
timeDuration.value = 1
|
||||
}
|
||||
updateTimeModdle()
|
||||
updateElementExtensions()
|
||||
}
|
||||
|
||||
const updateTimeModdle = () => {
|
||||
|
|
|
|||
|
|
@ -354,12 +354,13 @@ const resetTaskForm = () => {
|
|||
const changeCandidateStrategy = () => {
|
||||
userTaskForm.value.candidateParam = []
|
||||
deptLevel.value = 1
|
||||
if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
|
||||
// 特殊处理表单内用户字段,当只有发起人选项时应选中发起人
|
||||
if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
|
||||
userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
|
||||
}
|
||||
}
|
||||
// 注释 by 芋艿:这个交互很多用户反馈费解,https://t.zsxq.com/xNmas 所以暂时屏蔽
|
||||
// if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
|
||||
// // 特殊处理表单内用户字段,当只有发起人选项时应选中发起人
|
||||
// if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
|
||||
// userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
|
||||
// }
|
||||
// }
|
||||
updateElementTask()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import {
|
|||
ElCollapse,
|
||||
ElCollapseItem,
|
||||
ElCard,
|
||||
ElTreeSelect
|
||||
// ElFormItem,
|
||||
// ElOption
|
||||
} from 'element-plus'
|
||||
|
|
@ -97,6 +98,7 @@ const components = [
|
|||
ElTableColumn,
|
||||
ElTabPane,
|
||||
ElTabs,
|
||||
ElTreeSelect,
|
||||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem,
|
||||
|
|
@ -119,7 +121,7 @@ const components = [
|
|||
Editor,
|
||||
ElCollapse,
|
||||
ElCollapseItem,
|
||||
ElCard,
|
||||
ElCard
|
||||
]
|
||||
|
||||
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export const useUserStore = defineStore('admin-user', {
|
|||
userInfo = await getInfo()
|
||||
} catch (error) {}
|
||||
}
|
||||
this.permissions = new Set(userInfo.permissions)
|
||||
this.permissions = new Set(userInfo.permissions || []) // 兜底为 [] https://t.zsxq.com/xCJew
|
||||
this.roles = userInfo.roles
|
||||
this.user = userInfo.user
|
||||
this.isSetUser = true
|
||||
|
|
|
|||
|
|
@ -49,14 +49,17 @@ export const setConfAndFields2 = (
|
|||
detailPreview = detailPreview.value
|
||||
}
|
||||
|
||||
// 解析配置
|
||||
// 修复所有函数类型(解决设计器保存后函数变成字符串的问题)。例如说:
|
||||
// https://t.zsxq.com/rADff
|
||||
// https://t.zsxq.com/ZfbGt
|
||||
// https://t.zsxq.com/mHOoj
|
||||
// https://t.zsxq.com/BSylB
|
||||
const option = JSON.parse(conf)
|
||||
const rule = decodeFields(fields)
|
||||
|
||||
// 🔧 修复所有函数类型 - 解决设计器保存后函数变成字符串的问题
|
||||
const fixFunctions = (obj: any) => {
|
||||
if (obj && typeof obj === 'object') {
|
||||
Object.keys(obj).forEach(key => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
// 检查是否是函数相关的属性
|
||||
if (isFunctionProperty(key)) {
|
||||
// 如果不是函数类型,重新构建为函数
|
||||
|
|
@ -70,135 +73,108 @@ export const setConfAndFields2 = (
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否是函数属性
|
||||
const isFunctionProperty = (key: string): boolean => {
|
||||
const functionKeys = [
|
||||
'beforeFetch', // 请求前处理
|
||||
'afterFetch', // 请求后处理
|
||||
'onSubmit', // 表单提交
|
||||
'onReset', // 表单重置
|
||||
'onChange', // 值变化
|
||||
'onInput', // 输入事件
|
||||
'onClick', // 点击事件
|
||||
'onFocus', // 获取焦点
|
||||
'onBlur', // 失去焦点
|
||||
'onMounted', // 组件挂载
|
||||
'onCreated', // 组件创建
|
||||
'onReload', // 重新加载
|
||||
'remoteMethod', // 远程搜索方法
|
||||
'parseFunc', // 解析函数
|
||||
'validator', // 验证器
|
||||
'asyncValidator', // 异步验证器
|
||||
'formatter', // 格式化函数
|
||||
'parser', // 解析函数
|
||||
'beforeUpload', // 上传前处理
|
||||
'onSuccess', // 成功回调
|
||||
'onError', // 错误回调
|
||||
'onProgress', // 进度回调
|
||||
'onPreview', // 预览回调
|
||||
'onRemove', // 移除回调
|
||||
'onExceed', // 超出限制回调
|
||||
'filterMethod', // 过滤方法
|
||||
'sortMethod', // 排序方法
|
||||
'loadData', // 加载数据
|
||||
'renderContent', // 渲染内容
|
||||
'render' // 渲染函数
|
||||
'beforeFetch', // 请求前处理
|
||||
'afterFetch', // 请求后处理
|
||||
'onSubmit', // 表单提交
|
||||
'onReset', // 表单重置
|
||||
'onChange', // 值变化
|
||||
'onInput', // 输入事件
|
||||
'onClick', // 点击事件
|
||||
'onFocus', // 获取焦点
|
||||
'onBlur', // 失去焦点
|
||||
'onMounted', // 组件挂载
|
||||
'onCreated', // 组件创建
|
||||
'onReload', // 重新加载
|
||||
'remoteMethod', // 远程搜索方法
|
||||
'parseFunc', // 解析函数
|
||||
'validator', // 验证器
|
||||
'asyncValidator', // 异步验证器
|
||||
'formatter', // 格式化函数
|
||||
'parser', // 解析函数
|
||||
'beforeUpload', // 上传前处理
|
||||
'onSuccess', // 成功回调
|
||||
'onError', // 错误回调
|
||||
'onProgress', // 进度回调
|
||||
'onPreview', // 预览回调
|
||||
'onRemove', // 移除回调
|
||||
'onExceed', // 超出限制回调
|
||||
'filterMethod', // 过滤方法
|
||||
'sortMethod', // 排序方法
|
||||
'loadData', // 加载数据
|
||||
'renderContent', // 渲染内容
|
||||
'render' // 渲染函数
|
||||
]
|
||||
|
||||
// 检查是否以函数相关前缀开头
|
||||
const functionPrefixes = ['on', 'before', 'after', 'handle']
|
||||
|
||||
return functionKeys.includes(key) ||
|
||||
functionPrefixes.some(prefix => key.startsWith(prefix))
|
||||
return functionKeys.includes(key) || functionPrefixes.some((prefix) => key.startsWith(prefix))
|
||||
}
|
||||
|
||||
// 根据函数名创建默认函数
|
||||
const createDefaultFunction = (key: string): Function => {
|
||||
switch (key) {
|
||||
case 'beforeFetch':
|
||||
return (config: any) => {
|
||||
console.log('beforeFetch被调用:', config)
|
||||
|
||||
// 添加认证头
|
||||
// 添加 Token 认证头。例如说:
|
||||
// https://t.zsxq.com/hK3FO
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
'Authorization': 'Bearer ' + token
|
||||
Authorization: 'Bearer ' + token
|
||||
}
|
||||
}
|
||||
|
||||
// 添加通用请求头
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
|
||||
// 添加时间戳防止缓存
|
||||
config.params = {
|
||||
...config.params,
|
||||
_t: Date.now()
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
case 'afterFetch':
|
||||
return (data: any) => {
|
||||
console.log('afterFetch被调用:', data)
|
||||
return data
|
||||
}
|
||||
|
||||
case 'onSubmit':
|
||||
return (formData: any) => {
|
||||
console.log('onSubmit被调用:', formData)
|
||||
return (_formData: any) => {
|
||||
return true
|
||||
}
|
||||
|
||||
case 'onReset':
|
||||
return () => {
|
||||
console.log('onReset被调用')
|
||||
return true
|
||||
}
|
||||
|
||||
case 'onChange':
|
||||
return (value: any, oldValue: any) => {
|
||||
console.log('onChange被调用:', { value, oldValue })
|
||||
}
|
||||
|
||||
return (_value: any, _oldValue: any) => {}
|
||||
case 'remoteMethod':
|
||||
return (query: string) => {
|
||||
console.log('remoteMethod被调用:', query)
|
||||
}
|
||||
|
||||
case 'parseFunc':
|
||||
return (data: any) => {
|
||||
console.log('parseFunc被调用:', data)
|
||||
// 默认解析逻辑:如果是数组直接返回,否则尝试获取list属性
|
||||
if (Array.isArray(data)) {
|
||||
return data
|
||||
}
|
||||
return data?.list || data?.data || []
|
||||
}
|
||||
|
||||
case 'validator':
|
||||
return (rule: any, value: any, callback: Function) => {
|
||||
console.log('validator被调用:', { rule, value })
|
||||
return (_rule: any, _value: any, callback: Function) => {
|
||||
callback()
|
||||
}
|
||||
|
||||
case 'beforeUpload':
|
||||
return (file: any) => {
|
||||
console.log('beforeUpload被调用:', file)
|
||||
return (_file: any) => {
|
||||
return true
|
||||
}
|
||||
|
||||
default:
|
||||
// 通用默认函数
|
||||
return (...args: any[]) => {
|
||||
console.log(`${key}被调用:`, args)
|
||||
// 对于事件处理函数,返回true表示继续执行
|
||||
if (key.startsWith('on') || key.startsWith('handle')) {
|
||||
return true
|
||||
|
|
@ -208,11 +184,9 @@ export const setConfAndFields2 = (
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修复option中的所有函数
|
||||
// 修复 option 中的所有函数
|
||||
fixFunctions(option)
|
||||
|
||||
// 修复rule中的所有函数(包括组件的props)
|
||||
// 修复 rule 中的所有函数(包括组件的 props)
|
||||
if (Array.isArray(rule)) {
|
||||
rule.forEach((item: any) => {
|
||||
fixFunctions(item)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
|
|||
if (!route.children && route.parentId == 0 && route.component) {
|
||||
data.component = Layout
|
||||
data.meta = {
|
||||
hidden: meta.hidden,
|
||||
hidden: meta.hidden
|
||||
}
|
||||
data.name = toCamelCase(route.path, true) + 'Parent'
|
||||
data.redirect = ''
|
||||
|
|
@ -170,8 +170,9 @@ const generateRoutePath = (parentPath: string, path: string) => {
|
|||
}
|
||||
export const pathResolve = (parentPath: string, path: string) => {
|
||||
if (isUrl(path)) return path
|
||||
const childPath = path.startsWith('/') || !path ? path : `/${path}`
|
||||
return `${parentPath}${childPath}`.replace(/\/\//g, '/')
|
||||
if (!path) return parentPath // 修复 path 为空时返回 parentPath,避免拼接出错 https://t.zsxq.com/QVr6b
|
||||
const childPath = path.startsWith('/') ? path : `/${path}`
|
||||
return `${parentPath}${childPath}`.replace(/\/+/g, '/')
|
||||
}
|
||||
|
||||
// 路由降级
|
||||
|
|
|
|||
|
|
@ -118,4 +118,4 @@ $prefix-cls: #{$namespace}-login;
|
|||
background-color: var(--login-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@
|
|||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<el-row class="mx-[-10px]">
|
||||
<!-- 租户名 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
<LoginFormTitle class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="resetPasswordData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="resetPasswordData.tenantName"
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 手机号 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="mobile">
|
||||
<el-input
|
||||
v-model="resetPasswordData.mobile"
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
@success="getSmsCode"
|
||||
/>
|
||||
<!-- 验证码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="code">
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="24">
|
||||
|
|
@ -73,44 +73,44 @@
|
|||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="password">
|
||||
<InputPassword
|
||||
v-model="resetPasswordData.password"
|
||||
:placeholder="t('login.passwordPlaceholder')"
|
||||
style="width: 100%"
|
||||
strength="true"
|
||||
class="w-full"
|
||||
:strength="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="check_password">
|
||||
<InputPassword
|
||||
v-model="resetPasswordData.check_password"
|
||||
:placeholder="t('login.checkPassword')"
|
||||
style="width: 100%"
|
||||
strength="true"
|
||||
class="w-full"
|
||||
:strength="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 登录按钮 / 返回按钮 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.resetPassword')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
type="primary"
|
||||
@click="resetPassword()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.backLogin')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
@click="handleBackLogin()"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
@ -134,7 +134,7 @@ const verify = ref()
|
|||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const { currentRoute, push } = useRouter()
|
||||
const { currentRoute } = useRouter()
|
||||
const formSmsResetPassword = ref()
|
||||
const loginLoading = ref(false)
|
||||
const iconHouse = useIcon({ icon: 'ep:house' })
|
||||
|
|
@ -145,7 +145,7 @@ const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
|
|||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
|
||||
|
||||
const validatePass2 = (rule, value, callback) => {
|
||||
const validatePass2 = (_rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== resetPasswordData.password) {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-row class="mx-[-10px]">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
<LoginFormTitle class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.username"
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.password"
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
</el-col>
|
||||
<el-col
|
||||
:span="24"
|
||||
style="padding-right: 10px; padding-left: 10px; margin-top: -20px; margin-bottom: -20px"
|
||||
class="px-10px mt-[-20px] mb-[-20px]"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-row justify="space-between" style="width: 100%">
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
</el-col>
|
||||
<el-col :offset="6" :span="12">
|
||||
<el-link
|
||||
style="float: right"
|
||||
class="float-right"
|
||||
type="primary"
|
||||
@click="setLoginState(LoginStateEnum.RESET_PASSWORD)"
|
||||
>
|
||||
|
|
@ -70,12 +70,12 @@
|
|||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.login')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
type="primary"
|
||||
@click="getCode()"
|
||||
/>
|
||||
|
|
@ -89,27 +89,27 @@
|
|||
mode="pop"
|
||||
@success="handleLogin"
|
||||
/>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="8">
|
||||
<XButton
|
||||
:title="t('login.btnMobile')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
@click="setLoginState(LoginStateEnum.MOBILE)"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<XButton
|
||||
:title="t('login.btnQRCode')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
@click="setLoginState(LoginStateEnum.QR_CODE)"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<XButton
|
||||
:title="t('login.btnRegister')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
@click="setLoginState(LoginStateEnum.REGISTER)"
|
||||
/>
|
||||
</el-col>
|
||||
|
|
@ -117,9 +117,9 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<div class="w-[100%] flex justify-between">
|
||||
<div class="w-full flex justify-between">
|
||||
<Icon
|
||||
v-for="(item, key) in socialList"
|
||||
:key="key"
|
||||
|
|
@ -133,9 +133,9 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-divider content-position="center">萌新必读</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<div class="w-[100%] flex justify-between">
|
||||
<div class="w-full flex justify-between">
|
||||
<el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
|
||||
<el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
|
||||
<el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
|
||||
|
|
@ -241,11 +241,13 @@ const getLoginFormCache = () => {
|
|||
}
|
||||
// 根据域名,获得租户信息
|
||||
const getTenantByWebsite = async () => {
|
||||
const website = location.host
|
||||
const res = await LoginApi.getTenantByWebsite(website)
|
||||
if (res) {
|
||||
loginData.loginForm.tenantName = res.name
|
||||
authUtil.setTenantId(res.id)
|
||||
if (loginData.tenantEnable === 'true') {
|
||||
const website = location.host
|
||||
const res = await LoginApi.getTenantByWebsite(website)
|
||||
if (res) {
|
||||
loginData.loginForm.tenantName = res.name
|
||||
authUtil.setTenantId(res.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
const loading = ref() // ElLoading.service 返回的实例
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@
|
|||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<el-row class="mx-[-10px]">
|
||||
<!-- 租户名 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
<LoginFormTitle class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 手机号 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="mobileNumber">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.mobileNumber"
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 验证码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="code">
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="24">
|
||||
|
|
@ -68,23 +68,23 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 登录按钮 / 返回按钮 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.login')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
type="primary"
|
||||
@click="signIn()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.backLogin')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
@click="handleBackLogin()"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<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-row v-show="getShow" class="login-form mx-[-10px]">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<LoginFormTitle class="w-full" />
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-card class="mb-10px text-center" shadow="hover">
|
||||
<Qrcode :logo="logoImg" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<div class="mt-15px w-[100%]">
|
||||
<XButton :title="t('login.backLogin')" class="w-[100%]" @click="handleBackLogin()" />
|
||||
<el-col :span="24" class="px-10px">
|
||||
<div class="mt-4 w-full">
|
||||
<XButton :title="t('login.backLogin')" class="w-full" @click="handleBackLogin()" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-row class="mx-[-10px]">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
<LoginFormTitle class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="registerData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="registerData.registerForm.tenantName"
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="registerData.registerForm.username"
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="registerData.registerForm.nickname"
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="registerData.registerForm.password"
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="registerData.registerForm.confirmPassword"
|
||||
|
|
@ -73,12 +73,12 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.register')"
|
||||
class="w-[100%]"
|
||||
class="w-full"
|
||||
type="primary"
|
||||
@click="getCode()"
|
||||
/>
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
@success="handleRegister"
|
||||
/>
|
||||
</el-row>
|
||||
<XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" />
|
||||
<XButton :title="t('login.hasUser')" class="w-full" @click="handleBackLogin()" />
|
||||
</el-form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
|
@ -123,7 +123,7 @@ const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文
|
|||
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
const equalToPassword = (_rule, value, callback) => {
|
||||
if (registerData.registerForm.password !== value) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
} else {
|
||||
|
|
@ -233,11 +233,13 @@ const getTenantId = async () => {
|
|||
|
||||
// 根据域名,获得租户信息
|
||||
const getTenantByWebsite = async () => {
|
||||
const website = location.host
|
||||
const res = await LoginApi.getTenantByWebsite(website)
|
||||
if (res) {
|
||||
registerData.registerForm.tenantName = res.name
|
||||
authUtil.setTenantId(res.id)
|
||||
if (registerData.tenantEnable === 'true') {
|
||||
const website = location.host
|
||||
const res = await LoginApi.getTenantByWebsite(website)
|
||||
if (res) {
|
||||
registerData.registerForm.tenantName = res.name
|
||||
authUtil.setTenantId(res.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
const loading = ref() // ElLoading.service 返回的实例
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-show="ssoVisible" class="form-cont">
|
||||
<!-- 应用名 -->
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
<LoginFormTitle class="w-full" />
|
||||
<el-tabs class="form" style="float: none" value="uname">
|
||||
<el-tab-pane :label="client.name" name="uname" />
|
||||
</el-tabs>
|
||||
|
|
@ -15,17 +15,17 @@
|
|||
v-for="scope in queryParams.scopes"
|
||||
:key="scope"
|
||||
:value="scope"
|
||||
style="display: block; margin-bottom: -10px"
|
||||
class="block mb-[-10px]"
|
||||
>
|
||||
{{ formatScope(scope) }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<!-- 下方的登录按钮 -->
|
||||
<el-form-item class="w-1/1">
|
||||
<el-form-item class="w-full">
|
||||
<el-button
|
||||
:loading="formLoading"
|
||||
class="w-6/10"
|
||||
class="w-3/5"
|
||||
type="primary"
|
||||
@click.prevent="handleAuthorize(true)"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import { useUserStore } from '@/store/modules/user'
|
|||
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||
import { UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||
|
||||
// TODO @芋艿:合并到 ProfileUser 组件中,更简洁一点
|
||||
defineOptions({ name: 'UserAvatar' })
|
||||
|
||||
defineProps({
|
||||
|
|
@ -30,10 +29,12 @@ const userStore = useUserStore()
|
|||
const cropperRef = ref()
|
||||
const handelUpload = async ({ data }) => {
|
||||
const { httpRequest } = useUpload()
|
||||
const avatar = ((await httpRequest({
|
||||
file: data,
|
||||
filename: 'avatar.png',
|
||||
} as UploadRequestOptions)) as unknown as { data: string }).data
|
||||
const avatar = (
|
||||
(await httpRequest({
|
||||
file: data,
|
||||
filename: 'avatar.png'
|
||||
} as UploadRequestOptions)) as unknown as { data: string }
|
||||
).data
|
||||
await updateUserProfile({ avatar })
|
||||
|
||||
// 关闭弹窗,并更新 userStore
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { SystemUserSocialTypeEnum } from '@/utils/constants'
|
||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
|
||||
import { getBindSocialUserList } from '@/api/system/social/user'
|
||||
import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser'
|
||||
|
||||
defineOptions({ name: 'UserSocial' })
|
||||
|
|
@ -32,19 +32,19 @@ defineProps<{
|
|||
}>()
|
||||
const message = useMessage()
|
||||
const socialUsers = ref<any[]>([])
|
||||
const userInfo = ref<ProfileVO>()
|
||||
|
||||
const initSocial = async () => {
|
||||
socialUsers.value = [] // 重置避免无限增长
|
||||
const res = await getUserProfile()
|
||||
userInfo.value = res
|
||||
// 获取已绑定的社交用户列表
|
||||
const bindSocialUserList = await getBindSocialUserList()
|
||||
// 检查该社交平台是否已绑定
|
||||
for (const i in SystemUserSocialTypeEnum) {
|
||||
const socialUser = { ...SystemUserSocialTypeEnum[i] }
|
||||
socialUsers.value.push(socialUser)
|
||||
if (userInfo.value?.socialUsers) {
|
||||
for (const j in userInfo.value.socialUsers) {
|
||||
if (socialUser.type === userInfo.value.socialUsers[j].type) {
|
||||
socialUser.openid = userInfo.value.socialUsers[j].openid
|
||||
if (bindSocialUserList && bindSocialUserList.length > 0) {
|
||||
for (const bindUser of bindSocialUserList) {
|
||||
if (socialUser.type === bindUser.type) {
|
||||
socialUser.openid = bindUser.openid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -391,7 +391,8 @@ onMounted(async () => {
|
|||
line-height: 30px;
|
||||
|
||||
&.active {
|
||||
background-color: #e6e6e6;
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 1px solid var(--el-color-primary-light-7);
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
|
|
@ -409,7 +410,7 @@ onMounted(async () => {
|
|||
max-width: 220px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(0, 0, 0, 0.77);
|
||||
color: var(--el-text-color-regular);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -430,7 +431,7 @@ onMounted(async () => {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-items: center;
|
||||
color: #606266;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.btn {
|
||||
margin: 0;
|
||||
|
|
@ -447,8 +448,8 @@ onMounted(async () => {
|
|||
right: 0;
|
||||
//width: 100%;
|
||||
padding: 0 20px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8);
|
||||
background-color: var(--el-fill-color-extra-light);
|
||||
box-shadow: 0 0 1px 1px var(--el-border-color-lighter);
|
||||
line-height: 35px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
@ -458,7 +459,7 @@ onMounted(async () => {
|
|||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #606266;
|
||||
color: var(--el-text-color-regular);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
|
|
|||
|
|
@ -215,13 +215,13 @@ onMounted(async () => {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-wrap: break-word;
|
||||
background-color: rgba(228, 228, 228, 0.8);
|
||||
box-shadow: 0 0 0 1px rgba(228, 228, 228, 0.8);
|
||||
background-color: var(--el-fill-color-light);
|
||||
box-shadow: 0 0 0 1px var(--el-border-color-light);
|
||||
border-radius: 10px;
|
||||
padding: 10px 10px 5px 10px;
|
||||
|
||||
.left-text {
|
||||
color: #393939;
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -232,10 +232,10 @@ onMounted(async () => {
|
|||
|
||||
.right-text {
|
||||
font-size: 0.95rem;
|
||||
color: #fff;
|
||||
color: var(--el-color-white);
|
||||
display: inline;
|
||||
background-color: #267fff;
|
||||
box-shadow: 0 0 0 1px #267fff;
|
||||
background-color: var(--el-color-primary);
|
||||
box-shadow: 0 0 0 1px var(--el-color-primary);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
width: auto;
|
||||
|
|
@ -270,7 +270,7 @@ onMounted(async () => {
|
|||
|
||||
.btn-cus:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f6f6f6;
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ defineProps({
|
|||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
background-color: #ececec;
|
||||
background-color: var(--el-bg-color-page);
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
color: #3e3e3e;
|
||||
color: var(--el-text-color-primary);
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@
|
|||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="['edit', role]">
|
||||
<Icon icon="ep:edit" color="#787878" />编辑
|
||||
<Icon icon="ep:edit" color="var(--el-text-color-placeholder)" />编辑
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="['delete', role]" style="color: red">
|
||||
<Icon icon="ep:delete" color="red" />删除
|
||||
<el-dropdown-item :command="['delete', role]" style="color: var(--el-color-danger)">
|
||||
<Icon icon="ep:delete" color="var(--el-color-danger)" />删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
|
|
@ -153,13 +153,13 @@ const handleTabsScroll = async () => {
|
|||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #3e3e3e;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #6a6a6a;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
@click="handlerAddRole"
|
||||
class="ml-20px"
|
||||
>
|
||||
<Icon icon="ep:user" style="margin-right: 5px;" />
|
||||
<Icon icon="ep:user" style="margin-right: 5px" />
|
||||
添加角色
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -64,15 +64,15 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import RoleHeader from './RoleHeader.vue'
|
||||
import RoleList from './RoleList.vue'
|
||||
import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue'
|
||||
import RoleCategoryList from './RoleCategoryList.vue'
|
||||
import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole'
|
||||
import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation'
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import {TabsPaneContext} from 'element-plus'
|
||||
import { ChatRoleApi, ChatRolePageReqVO, ChatRoleVO } from '@/api/ai/model/chatRole'
|
||||
import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
|
||||
const router = useRouter() // 路由对象
|
||||
|
||||
|
|
@ -244,7 +244,7 @@ onMounted(async () => {
|
|||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #ffffff;
|
||||
background-color: var(--el-bg-color);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,16 @@
|
|||
<Icon icon="ep:setting" class="ml-10px" />
|
||||
</el-button>
|
||||
<el-button size="small" class="btn" @click="handlerMessageClear">
|
||||
<Icon icon="heroicons-outline:archive-box-x-mark" color="#787878" />
|
||||
<Icon
|
||||
icon="heroicons-outline:archive-box-x-mark"
|
||||
color="var(--el-text-color-placeholder)"
|
||||
/>
|
||||
</el-button>
|
||||
<el-button size="small" class="btn">
|
||||
<Icon icon="ep:download" color="#787878" />
|
||||
<Icon icon="ep:download" color="var(--el-text-color-placeholder)" />
|
||||
</el-button>
|
||||
<el-button size="small" class="btn" @click="handleGoTopMessage">
|
||||
<Icon icon="ep:top" color="#787878" />
|
||||
<Icon icon="ep:top" color="var(--el-text-color-placeholder)" />
|
||||
</el-button>
|
||||
</div>
|
||||
</el-header>
|
||||
|
|
@ -613,7 +616,8 @@ onMounted(async () => {
|
|||
line-height: 30px;
|
||||
|
||||
&.active {
|
||||
background-color: #e6e6e6;
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 1px solid var(--el-color-primary-light-7);
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
|
|
@ -649,7 +653,7 @@ onMounted(async () => {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-items: center;
|
||||
color: #606266;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.el-icon {
|
||||
margin-right: 5px;
|
||||
|
|
@ -669,7 +673,7 @@ onMounted(async () => {
|
|||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #606266;
|
||||
color: var(--el-text-color-regular);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
|
@ -683,15 +687,15 @@ onMounted(async () => {
|
|||
|
||||
// 头部
|
||||
.detail-container {
|
||||
background: #ffffff;
|
||||
background: var(--el-bg-color);
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fbfbfb;
|
||||
box-shadow: 0 0 0 0 #dcdfe6;
|
||||
background: var(--el-bg-color-page);
|
||||
box-shadow: 0 0 0 0 var(--el-border-color-light);
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
|
|
@ -744,7 +748,7 @@ onMounted(async () => {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
border: 1px solid #e3e3e3;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 10px;
|
||||
margin: 10px 20px 20px 20px;
|
||||
padding: 9px 10px;
|
||||
|
|
|
|||
|
|
@ -510,10 +510,15 @@ const isManagerUser = (row: any) => {
|
|||
|
||||
/** 处理模型的排序 **/
|
||||
const handleModelSort = () => {
|
||||
// 保存初始数据
|
||||
originalData.value = cloneDeep(props.categoryInfo.modelList)
|
||||
isModelSorting.value = true
|
||||
initSort()
|
||||
if (isModelSorting.value) {
|
||||
// 如果已经在排序状态,则取消排序
|
||||
handleModelSortCancel()
|
||||
} else {
|
||||
// 保存初始数据
|
||||
originalData.value = cloneDeep(props.categoryInfo.modelList)
|
||||
isModelSorting.value = true
|
||||
initSort()
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理模型的排序提交 */
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
</div>
|
||||
<div v-if="modelData.startUserType === 2" class="mt-2 flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="dept in selectedStartDepts"
|
||||
v-for="dept in selectedStartDepts"
|
||||
:key="dept.id"
|
||||
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
|
||||
>
|
||||
|
|
@ -186,7 +186,23 @@ const currentSelectType = ref<'start' | 'manager'>('start')
|
|||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
|
||||
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
|
||||
key: [
|
||||
{ required: true, message: '流程标识不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
if (!value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if (!/^[a-zA-Z_][\-_.0-9_a-zA-Z$]*$/.test(value)) {
|
||||
callback(new Error('只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头'))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
|
|
|
|||
|
|
@ -232,6 +232,34 @@ import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/co
|
|||
import HttpRequestSetting from '@/components/SimpleProcessDesignerV2/src/nodes-config/components/HttpRequestSetting.vue'
|
||||
|
||||
const modelData = defineModel<any>()
|
||||
const formFields = ref<string[]>([])
|
||||
|
||||
const props = defineProps({
|
||||
// 流程表单 ID
|
||||
modelFormId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: undefined,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 监听 modelFormId 变化
|
||||
watch(
|
||||
() => props.modelFormId,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
const form = await FormApi.getForm(newVal);
|
||||
formFields.value = form?.fields;
|
||||
} else {
|
||||
// 如果 modelFormId 为空,清空表单字段
|
||||
formFields.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
// 暴露给子组件使用
|
||||
provide('formFields', formFields)
|
||||
|
||||
/** 自定义 ID 流程编码 */
|
||||
const timeOptions = ref([
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
<template v-else>
|
||||
<SimpleModelDesign
|
||||
v-if="showDesigner"
|
||||
:model-id="modelData.id"
|
||||
:model-key="modelData.key"
|
||||
:model-name="modelData.name"
|
||||
:model-form-id="modelData.formId"
|
||||
:model-form-type="modelData.formType"
|
||||
:start-user-ids="modelData.startUserIds"
|
||||
:start-dept-ids="modelData.startDeptIds"
|
||||
@success="handleDesignSuccess"
|
||||
|
|
|
|||
|
|
@ -77,7 +77,10 @@
|
|||
|
||||
<!-- 第四步:更多设置 -->
|
||||
<div v-show="currentStep === 3" class="mx-auto w-700px">
|
||||
<ExtraSettings v-model="formData" ref="extraSettingsRef" />
|
||||
<ExtraSettings
|
||||
ref="extraSettingsRef"
|
||||
v-model="formData"
|
||||
:model-form-id="formData.formId"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -216,6 +219,16 @@ const initData = async () => {
|
|||
// 特殊:复制场景
|
||||
if (route.params.type === 'copy') {
|
||||
delete formData.value.id
|
||||
if (formData.value.bpmnXml) {
|
||||
formData.value.bpmnXml = formData.value.bpmnXml.replaceAll(
|
||||
formData.value.name,
|
||||
formData.value.name + '副本'
|
||||
)
|
||||
formData.value.bpmnXml = formData.value.bpmnXml.replaceAll(
|
||||
formData.value.key,
|
||||
formData.value.key + '_copy'
|
||||
)
|
||||
}
|
||||
formData.value.name += '副本'
|
||||
formData.value.key += '_copy'
|
||||
tagsView.setTitle('复制流程')
|
||||
|
|
|
|||
|
|
@ -209,15 +209,18 @@ onActivated(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.el-table--fit .el-table__inner-wrapper:before {
|
||||
.el-table--fit .el-table__inner-wrapper::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.el-form--inline .el-form-item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,6 +685,7 @@ watch(
|
|||
|
||||
/** 弹出气泡卡 */
|
||||
const openPopover = async (type: string) => {
|
||||
if (popOverVisible.value[type] === true) return
|
||||
if (type === 'approve') {
|
||||
// 校验流程表单
|
||||
const valid = await validateNormalForm()
|
||||
|
|
|
|||
|
|
@ -38,7 +38,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="activity.nodeType === NodeType.CHILD_PROCESS_NODE">
|
||||
<el-button type="primary" plain size="small" @click="handleChildProcess(activity)">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="small"
|
||||
@click="handleChildProcess(activity)"
|
||||
:disabled="!activity.processInstanceId"
|
||||
>
|
||||
查看子流程
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -319,7 +325,9 @@ const handleUserSelectConfirm = (activityId: string, userList: any[]) => {
|
|||
|
||||
/** 跳转子流程 */
|
||||
const handleChildProcess = (activity: any) => {
|
||||
// TODO @lesan:貌似跳不过去?!
|
||||
if (!activity.processInstanceId) {
|
||||
return
|
||||
}
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
<template>
|
||||
<ContentWrap :bodyStyle="{ padding: '20px 16px' }">
|
||||
<SimpleProcessDesigner
|
||||
:model-id="modelId"
|
||||
:model-key="modelKey"
|
||||
:model-name="modelName"
|
||||
@success="handleSuccess"
|
||||
:model-form-id="modelFormId"
|
||||
:model-form-type="modelFormType"
|
||||
:start-user-ids="startUserIds"
|
||||
:start-dept-ids="startDeptIds"
|
||||
@success="handleSuccess"
|
||||
ref="designerRef"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
|
@ -19,9 +18,9 @@ defineOptions({
|
|||
})
|
||||
|
||||
defineProps<{
|
||||
modelId?: string
|
||||
modelKey?: string
|
||||
modelName?: string
|
||||
modelFormId?: number
|
||||
modelFormType?: number
|
||||
startUserIds?: number[]
|
||||
startDeptIds?: number[]
|
||||
}>()
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ import type { UploadUserFile } from 'element-plus'
|
|||
import * as UserApi from '@/api/system/user'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
defineOptions({ name: 'SystemUserImportForm' })
|
||||
defineOptions({ name: 'CrmCustomerImportForm' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,38 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="跟进内容" prop="content" />
|
||||
<el-table-column label="图片" align="center">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.picUrls && scope.row.picUrls.length > 0" class="flex">
|
||||
<el-image
|
||||
v-for="(url, index) in scope.row.picUrls"
|
||||
:key="index"
|
||||
:src="url"
|
||||
:preview-src-list="scope.row.picUrls"
|
||||
class="w-10 h-10 mr-1"
|
||||
:initial-index="index"
|
||||
fit="cover"
|
||||
preview-teleported
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" align="center">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.fileUrls && scope.row.fileUrls.length > 0" class="flex flex-col">
|
||||
<el-link
|
||||
v-for="(url, index) in scope.row.fileUrls"
|
||||
:key="index"
|
||||
:href="url"
|
||||
type="primary"
|
||||
target="_blank"
|
||||
download
|
||||
>
|
||||
{{ getFileName(url) }}
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
|
@ -97,6 +129,14 @@ import { BizTypeEnum } from '@/api/crm/permission'
|
|||
|
||||
/** 跟进记录列表 */
|
||||
defineOptions({ name: 'FollowUpRecord' })
|
||||
|
||||
const getFileName = (url: string) => {
|
||||
if (!url) {
|
||||
return ''
|
||||
}
|
||||
return url.substring(url.lastIndexOf('/') + 1)
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
bizType: number
|
||||
bizId: number
|
||||
|
|
|
|||
|
|
@ -107,16 +107,15 @@ const initGiveCouponList = async () => {
|
|||
|
||||
/** 设置赠送的优惠券 */
|
||||
const setGiveCouponList = () => {
|
||||
if (isEmpty(rewardRule.value) || isEmpty(list.value)) {
|
||||
if (isEmpty(rewardRule.value)) {
|
||||
return
|
||||
}
|
||||
// 核心:清空 rewardRule.value.giveCouponTemplateCounts,解决删除不生效的问题
|
||||
rewardRule.value.giveCouponTemplateCounts = {}
|
||||
|
||||
// 设置优惠券和其数量的对应
|
||||
list.value.forEach((rule) => {
|
||||
if (!rewardRule.value.giveCouponTemplateCounts) {
|
||||
rewardRule.value.giveCouponTemplateCounts = {}
|
||||
}
|
||||
rewardRule.value.giveCouponTemplateCounts[rule.id] = rule.giveCount!
|
||||
rewardRule.value.giveCouponTemplateCounts![rule.id] = rule.giveCount!
|
||||
})
|
||||
}
|
||||
defineExpose({ setGiveCouponList })
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ import { MenuVO } from '@/api/system/menu'
|
|||
import MenuForm from './MenuForm.vue'
|
||||
import DictTag from '@/components/DictTag/src/DictTag.vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { ElButton, TableV2FixedDir } from 'element-plus'
|
||||
import { ElButton, TableV2FixedDir, ElSwitch } from 'element-plus'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
|
|
@ -175,7 +175,7 @@ const columns = [
|
|||
fixed: TableV2FixedDir.RIGHT,
|
||||
cellRenderer: ({ rowData }) => {
|
||||
// 定义按钮列表
|
||||
const buttons = []
|
||||
const buttons: InstanceType<typeof ElButton>[] = []
|
||||
|
||||
// 检查权限并添加按钮
|
||||
if (checkPermi(['system:menu:update'])) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue