vue更新3.5,以及mime打更新

This commit is contained in:
2024-09-06 10:48:22 +08:00
parent 9984041eec
commit 3914762c85
117 changed files with 4348 additions and 8000 deletions

View File

@@ -1,9 +1,10 @@
<template>
<template v-for="row in props.columns" :key="row[options.pk]">
<template v-if="!row.hide">
<template v-if="isFunction(row.hide) ? row.hide() : !row.hide">
<a-table-column
:title="row.title"
:width="row.width"
:min-width="row.minWidth"
:ellipsis="row.ellipsis ?? true"
:filterable="row.filterable"
:cell-class="row.cellClass"
@@ -38,6 +39,7 @@
:title="row.title"
:data-index="row.dataIndex"
:width="row.width"
:min-width="row.minWidth"
:ellipsis="row.ellipsis ?? true"
:filterable="row.filterable"
:cell-class="row.cellClass"
@@ -48,12 +50,15 @@
:header-cell-style="row.headerCellStyle"
:body-cell-style="row.bodyCellStyle"
:summary-cell-style="row.summaryCellStyle"
:tooltip="row.dataIndex === '__operation' ? false : row.tooltip ?? true"
:tooltip="row.dataIndex === '__operation' ? false : (row.tooltip ?? true)"
:align="row.align || 'left'"
:fixed="row.fixed"
:sortable="row.sortable"
v-else
>
<template #title>
<slot :name="`tableTitle-${row.dataIndex}`" v-bind="{ column: row }">{{ row.title }}</slot>
</template>
<template #cell="{ record, column, rowIndex }">
<!-- 操作栏 -->
<template v-if="row.dataIndex === '__operation'">
@@ -61,22 +66,41 @@
<a-space size="mini">
<slot name="operationBeforeExtend" v-bind="{ record, column, rowIndex }"></slot>
<slot name="operationCell" v-bind="{ record, column, rowIndex }">
<!-- <a-link
v-if="
options.see.show
&& ($common.auth(options.see.auth || [])
|| (options.see.role || []))
"
type="primary"
><icon-eye /> {{ options.see.text || '查看' }}</a-link> -->
<a-link
v-if="
(isFunction(options.see.show)
? options.see.show(record)
: options.see.show) && !props.isRecovery
"
v-auth="options.see.auth || []"
v-role="options.see.role || []"
type="primary"
:status="options.see.status || 'success'"
:style="{ color: options.see.color || '' }"
:disabled="
isFunction(options.see.disabled)
? options.see.disabled(record)
: options.see.disabled
"
@click="seeAction(record)"
><icon-eye /> {{ options.see.text || "查看" }}</a-link
>
<a-link
v-if="
(isFunction(options.edit.show)
? options.edit.show(record)
: options.edit.show) && !props.isRecovery
"
v-auth="options.edit.auth || []"
v-role="options.edit.role || []"
type="primary"
:status="options.edit.status || 'normal'"
:style="{ color: options.edit.color || '' }"
:disabled="
isFunction(options.edit.disabled)
? options.edit.disabled(record)
: options.edit.disabled
"
@click="editAction(record)"
>
<icon-edit /> {{ options.edit.text || "编辑" }}
@@ -91,6 +115,8 @@
? options.recovery.show(record)
: options.recovery.show) && props.isRecovery
"
v-auth="options.recovery.auth || []"
v-role="options.recovery.role || []"
>
<a-link type="primary"
><icon-undo /> {{ options.recovery.text || "恢复" }}
@@ -107,7 +133,18 @@
: options.delete.show
"
>
<a-link type="primary">
<a-link
type="primary"
:status="options.delete.status || 'danger'"
:style="{ color: options.delete.color || '' }"
v-auth="options.delete.auth || []"
v-role="options.delete.role || []"
:disabled="
isFunction(options.delete.disabled)
? options.delete.disabled(record)
: options.delete.disabled
"
>
<icon-delete />
{{
props.isRecovery
@@ -131,28 +168,66 @@
</template>
<slot :name="row.dataIndex" v-bind="{ record, column, rowIndex }" v-else>
<template v-if="row.dataIndex === '__index'">{{ getIndex(rowIndex) }}</template>
<template v-if="row.dict && row.dict.translation">
<template v-if="isArray(get(record, row.dataIndex))">
<a-tag v-for="item in get(record, row.dataIndex)" class="ml-1">{{
getDataIndex(row, item)
}}</a-tag>
<span :class="['relative', row?.quickEdit && allowQuickEdit(row.formType) ? 'flex' : '']">
<template v-if="row.isEdit">
<a-form>
<a-button-group class="flex">
<a-input
v-if="row?.formType === undefined || row.formType === 'input'"
v-bind="row"
v-model="record[row.dataIndex]"
/>
<a-input-number
v-if="row.formType === 'input-number'"
v-bind="row"
v-model="record[row.dataIndex]"
/>
<a-select
v-if="['select', 'radio'].includes(row.formType)"
v-bind="row"
v-model="record[row.dataIndex]"
:options="dicts[row.dataIndex]"
/>
<a-link
class="block"
v-if="row?.quickEdit && allowQuickEdit(row.formType) && row.isEdit === true"
@click="updateQuickEditData(row, record)"
>
<icon-check />
</a-link>
</a-button-group>
</a-form>
</template>
<a-tag v-else-if="row.dict.tagColors" :color="getTagColor(row, record)">
{{ getDataIndex(row, record) }}
</a-tag>
<a-tag v-else-if="row.dict.tagColor" :color="row.dict.tagColor">{{
getDataIndex(row, record)
}}</a-tag>
<span v-else>{{ getDataIndex(row, record) }}</span>
</template>
<template v-else-if="row.dataIndex && row.dataIndex.indexOf('.') !== -1">
{{ get(record, row.dataIndex) }}
</template>
<template v-else-if="row.formType === 'upload'">
<a-link @click="imageSee(row, record, row.dataIndex)"><icon-image /> 查看图片</a-link>
</template>
<template v-else>{{ record[row.dataIndex] }}</template>
<template v-else-if="row.dict && row.dict.translation">
<template v-if="isArray(get(record, row.dataIndex))">
<a-tag v-for="item in get(record, row.dataIndex)" class="ml-1">{{
getDataIndex(row, item)
}}</a-tag>
</template>
<a-tag v-else-if="row.dict.tagColors" :color="getTagColor(row, record)">
{{ getDataIndex(row, record) }}
</a-tag>
<a-tag v-else-if="row.dict.tagColor" :color="row.dict.tagColor">{{
getDataIndex(row, record)
}}</a-tag>
<span v-else>{{ getDataIndex(row, record) }}</span>
</template>
<template v-else-if="row.dataIndex && row.dataIndex.indexOf('.') !== -1">
{{ get(record, row.dataIndex) }}
</template>
<template v-else-if="row.formType === 'upload'">
<a-link @click="imageSee(row, record, row.dataIndex)"><icon-image /> 查看图片</a-link>
</template>
<template v-else>{{ record[row.dataIndex] }}</template>
<a-link
class="absolute top-1 right-0"
v-if="row?.quickEdit && allowQuickEdit(row.formType) && !row.isEdit"
@click="quickEdit(row, record)"
>
<icon-edit />
</a-link>
</span>
</slot>
</template>
</a-table-column>
@@ -161,7 +236,7 @@
</template>
<script setup>
import { inject } from "vue"
import { inject, ref, provide } from "vue"
import config from "@/config/crud"
import uploadConfig from "@/config/upload"
import { Message } from "@arco-design/web-vue"
@@ -170,15 +245,17 @@ import CustomRender from "../js/custom-render"
import tool from "@/utils/tool"
import commonApi from "@/api/common"
import formInput from "@cps/ma-form/formItem/form-input.vue"
const emit = defineEmits(["refresh", "showImage"])
const props = defineProps({
columns: Array,
isRecovery: Boolean,
crudFormRef: Object
})
const options = inject("options")
const requestParams = inject("requestParams")
const dicts = inject("dicts")
const dictTrans = inject("dictTrans")
const dictColors = inject("dictColors")
@@ -189,29 +266,15 @@ const imageSee = async (row, record, dataIndex) => {
return
}
if (!["id", "hash"].includes(row.returnType)) {
Message.info("该图片无法查看")
return
}
Message.info("获取图片中,请稍等...")
const res =
row.returnType === "id"
? await commonApi.getFileInfoById({ id: record.id })
: await commonApi.getFileInfoByHash({ hash: record.hash })
const result = res?.success ?? false
if (!result) {
Message.info("图片信息无法获取")
if (row.returnType === "hash") {
emit("showImage", tool.showFile(record[dataIndex]))
return
}
const isImage = res.data.mime_type.indexOf("image") > -1
result &&
emit(
"showImage",
isImage
? tool.attachUrl(res.data.url, uploadConfig.storageMode[res.data.storage_mode])
: "not-image.png"
)
if (row.returnType === "id") {
Message.info("该图片无法查看")
return
}
} else {
if (!record[row.dataIndex]) {
Message.info("无图片")
@@ -248,8 +311,21 @@ const getIndex = (rowIndex) => {
}
}
const seeAction = (record) => {
if (isFunction(options.beforeOpenSee) && !options.beforeOpenSee(record)) {
return false
}
if (options.see.action && isFunction(options.see.action)) {
options.see.action(record)
} else {
props.crudFormRef.see(record)
}
}
const editAction = (record) => {
isFunction(options.beforeOpenEdit) && options.beforeOpenEdit(record)
if (isFunction(options.beforeOpenEdit) && !options.beforeOpenEdit(record)) {
return false
}
if (options.edit.action && isFunction(options.edit.action)) {
options.edit.action(record)
} else {
@@ -257,6 +333,25 @@ const editAction = (record) => {
}
}
const allowQuickEdit = (formType) => ["select", "input", "input-number", "radio"].includes(formType ?? "input")
const quickEdit = (row, record) => {
if (row.formType === "select" || row.formType === "radio") {
record[row.dataIndex] = record[row.dataIndex].toString()
}
row.isEdit = true
}
const updateQuickEditData = async (row, record) => {
if (isFunction(options.beforeEdit) && !(await options.beforeEdit(record))) {
return false
}
const response = await options.edit.api(record[options.pk], record)
row.isEdit = false
isFunction(options.afterEdit) && (await options.afterEdit(response, record))
Message.success(response.message || `修改成功!`)
}
const recoveryAction = async (record) => {
const response = await options.recovery.api({ ids: [record[options.pk]] })
response.success && Message.success(response.message || `恢复成功!`)

View File

@@ -46,7 +46,7 @@
<script setup>
import { ref, inject, watch, nextTick } from "vue"
import checkAuth from "@/directives/auth/auth"
import { isArray } from "lodash"
import { isArray } from "lodash-es"
const left = ref(0)
const top = ref(0)
@@ -100,7 +100,11 @@ const openContextMenu = async (ev, record) => {
currentRow.value = record
await nextTick(() => {
const domHeight = document.querySelector(".ma-crud-contextmenu").offsetHeight
top.value = ev.clientY - domHeight
if (document.body.offsetHeight - ev.pageY < domHeight) {
top.value = ev.clientY - domHeight
} else {
top.value = ev.clientY
}
left.value = ev.clientX
})
}

View File

@@ -1,5 +1,6 @@
<template>
<!-- addon-before-cancel -->
<!-- 修改源码添加mask-closable属性 -->
<!-- 修改源码添加on-before-cancel属性 -->
<component
:is="componentName"
v-model:visible="dataVisible"
@@ -16,7 +17,7 @@
>
<template #title>{{ actionTitle }}</template>
<a-spin :loading="dataLoading" tip="加载中..." class="w-full">
<ma-form v-model="form" :columns="formColumns" :options="{ showButtons: false }" ref="maFormRef">
<ma-form v-model="form" :columns="formColumns" :options="formOptions" ref="maFormRef">
<template v-for="slot in Object.keys($slots)" #[slot]="component">
<slot :name="slot" v-bind="component" />
</template>
@@ -26,43 +27,53 @@
</template>
<script setup>
import { ref, toRaw, getCurrentInstance, inject, provide } from "vue"
import { ref, toRaw, inject, provide, nextTick } from "vue" // 这里多了nextTick
import { Message } from "@arco-design/web-vue"
import { containerItems } from "@cps/ma-form/js/utils"
import { isArray, isFunction, get, cloneDeep, isUndefined } from "lodash"
import { isArray, isFunction, get, cloneDeep, isUndefined } from "lodash-es"
import { useRouter } from "vue-router"
import tool from "@/utils/tool"
import { useFormStore } from "@/store/index"
const formStore = useFormStore()
const router = useRouter()
const app = getCurrentInstance().appContext.app
const formOptions = ref({ showButtons: false })
const maFormRef = ref()
const componentName = ref("a-modal")
const columns = inject("columns")
const layoutColumns = ref(new Map())
const options = inject("options")
const formColumns = ref([])
const currentAction = ref("")
const dataVisible = ref(false)
const form = ref({})
const actionTitle = ref("")
const dataLoading = ref(true)
const columns = inject("columns")
const options = inject("options")
// 修改源码新增事件beforeCancel和处理函数beforeCancel
const emit = defineEmits(["success", "error", "beforeCancel"])
const beforeCancel = () => {
emit("beforeCancel")
return true
}
provide("form", toRaw(form))
if (window.screen.width < 768) {
options.formOption.width = window.screen.width
options.formOption.isFull = true
}
const submit = async () => {
const formData = maFormRef.value.getFormData()
if (await maFormRef.value.validateForm()) {
return false
}
let response
// 在这里添加我们自定义的parameters注意判断options中是否有parameters-key
if (currentAction.value === "add") {
isFunction(options.beforeAdd) && (await options.beforeAdd(formData))
// 首先判断是否options.parameters存在
if (isFunction(options.beforeAdd) && !(await options.beforeAdd(formData))) {
return false
}
// 修改源码添加parameters参数
if (!options.parameters) {
response = await options.add.api(formData)
} else {
@@ -70,8 +81,9 @@ const submit = async () => {
}
isFunction(options.afterAdd) && (await options.afterAdd(response, formData))
} else {
isFunction(options.beforeEdit) && (await options.beforeEdit(formData))
// 编辑也需要更新
if (isFunction(options.beforeEdit) && !(await options.beforeEdit(formData))) {
return false
}
if (!options.parameters) {
response = await options.edit.api(formData[options.pk], formData)
} else {
@@ -84,6 +96,7 @@ const submit = async () => {
emit("success", response)
return true
} else if (response.success === false && (typeof response.code === "undefined" || response.code !== 200)) {
Message.clear()
Message.error(response.message || `${actionTitle.value}失败!`)
return false
}
@@ -92,7 +105,7 @@ const open = () => {
formColumns.value = []
layoutColumns.value = new Map()
init()
if (options.formOption.viewType === "tag") {
if (options.formOption.viewType === "tag" && currentAction.value !== "see") {
if (!options.formOption.tagId) {
Message.info("未配置 tagId")
return
@@ -103,6 +116,7 @@ const open = () => {
}
const config = {
options,
sourceColumns: columns.value,
formColumns: formColumns.value
}
@@ -128,7 +142,6 @@ const open = () => {
} else {
formStore.formList[options.formOption.tagId].editData[queryParams.key] = cloneDeep(form.value)
}
form.value = {}
router.push(`/openForm/${options.formOption.tagId}` + tool.httpBuild(queryParams, true))
} else {
@@ -136,17 +149,13 @@ const open = () => {
dataVisible.value = true
}
}
// ~~~~addMethod~~~~
const beforeCancel = () => {
emit("beforeCancel")
return true
}
const close = () => {
dataVisible.value = false
formColumns.value = []
form.value = {}
}
const add = () => {
const add = async () => {
// 修改源码:开始
const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"]
if (!actionTitle.value) {
actionTitle.value = "新增"
@@ -155,11 +164,15 @@ const add = () => {
} else {
actionTitle.value += "新增"
}
// 修改源码:结束
currentAction.value = "add"
formOptions.value["disabled"] = false
form.value = {}
open()
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
}
const edit = (data) => {
const edit = async (data) => {
// 修改源码:开始
const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"]
if (!actionTitle.value) {
actionTitle.value = "编辑"
@@ -168,32 +181,66 @@ const edit = (data) => {
} else {
actionTitle.value += "编辑"
}
// 修改源码:结束
currentAction.value = "edit"
formOptions.value["disabled"] = false
form.value = {}
for (let i in data) form.value[i] = data[i]
open(data)
if (options.edit.dataSource && options.edit.dataSource === "api") {
const response = await options.edit.dataSourceApi(data[options.pk])
if (response.success) {
form.value = response.data
open(response.data)
}
} else {
for (let i in data) form.value[i] = data[i]
open(data)
}
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
}
const see = async (data) => {
actionTitle.value = options.see.title ?? "查看"
currentAction.value = "see"
formOptions.value["disabled"] = true
form.value = {}
if (options.see.dataSource && options.see.dataSource === "api") {
const response = await options.see.dataSourceApi(data[options.pk])
if (response.success) {
form.value = response.data
open(response.data)
}
} else {
for (let i in data) form.value[i] = data[i]
open(data)
}
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
}
const init = () => {
const init = async () => {
dataLoading.value = true
const layout = JSON.parse(JSON.stringify(options?.formOption?.layout ?? []))
// const layout = options?.formOption?.layout ?? []
columns.map(async (item) => {
await columnItemHandle(item)
})
await Promise.all(
columns.value.map(async (item) => {
if (item.children && item.children.length > 0) {
await item.children.map(async (childItem) => {
await columnItemHandle(childItem)
})
} else {
await columnItemHandle(item)
}
})
)
// 设置表单布局
settingFormLayout(layout)
if (isArray(layout) && layout.length > 0) {
formColumns.value = layout
const excludeColumns = ["__index", "__operation"]
columns.map((item) => {
columns.value.map((item) => {
if (options.formExcludePk) excludeColumns.push(options.pk)
if (excludeColumns.includes(item.dataIndex)) return
!item.__formLayoutSetting && formColumns.value.push(item)
})
}
dataLoading.value = false
}
@@ -207,28 +254,33 @@ const columnItemHandle = async (item) => {
layoutColumns.value.set(item.dataIndex, item)
formColumns.value.push(item)
// 针对带点的数据处理
if (item.dataIndex && item.dataIndex.indexOf(".") > -1) {
form.value[item.dataIndex] = get(form.value, item.dataIndex)
}
// add 默认值处理
if (currentAction.value === "add") {
if (item.addDefaultValue && isFunction(item.addDefaultValue)) {
form.value[item.dataIndex] = await item.addDefaultValue(form.value)
} else if (typeof item.addDefaultValue != "undefined") {
form.value[item.dataIndex] = item.addDefaultValue
if (options.formOption.viewType !== "tag") {
// 针对带点的数据处理
if (item.dataIndex && item.dataIndex.indexOf(".") > -1) {
form.value[item.dataIndex] = get(form.value, item.dataIndex)
}
}
// edit 默认值处理
if (currentAction.value === "edit") {
if (item.editDefaultValue && isFunction(item.editDefaultValue)) {
form.value[item.dataIndex] = await item.editDefaultValue(form.value)
} else if (typeof item.editDefaultValue != "undefined") {
form.value[item.dataIndex] = item.editDefaultValue
// add 默认值处理
if (currentAction.value === "add") {
if (item.addDefaultValue && isFunction(item.addDefaultValue)) {
form.value[item.dataIndex] = await item.addDefaultValue(form.value)
} else if (typeof item.addDefaultValue != "undefined") {
form.value[item.dataIndex] = item.addDefaultValue
}
}
// edit 和 see 默认值处理
if (currentAction.value === "edit" || currentAction.value === "see") {
if (item.editDefaultValue && isFunction(item.editDefaultValue)) {
form.value[item.dataIndex] = await item.editDefaultValue(form.value)
} else if (typeof item.editDefaultValue != "undefined") {
form.value[item.dataIndex] = item.editDefaultValue
}
}
}
item.disabled = undefined
item.readonly = undefined
await nextTick()
// 其他处理
item.display = formItemShow(item)
item.disabled = formItemDisabled(item)
@@ -236,6 +288,7 @@ const columnItemHandle = async (item) => {
item.labelWidth = formItemLabelWidth(item)
item.rules = getRules(item)
}
const settingFormLayout = (layout) => {
if (!isArray(layout)) {
return
@@ -306,19 +359,22 @@ const settingFormLayout = (layout) => {
const formItemShow = (item) => {
if (currentAction.value === "add") {
return item.addDisplay !== false
return isFunction(item.addDisplay) ? item.addDisplay() !== false : item.addDisplay !== false
}
if (currentAction.value === "edit") {
return item.editDisplay !== false
if (currentAction.value === "edit" || currentAction.value === "see") {
return isFunction(item.editDisplay) ? item.editDisplay(form.value) !== false : item.editDisplay !== false
}
return item.display !== false
}
const formItemDisabled = (item) => {
if (currentAction.value === "add" && !isUndefined(item.addDisabled)) {
return item.addDisabled
return isFunction(item.addDisabled) ? item.addDisabled() : item.addDisabled
}
if (currentAction.value === "edit" && !isUndefined(item.editDisabled)) {
return item.editDisabled
return isFunction(item.editDisabled) ? item.editDisabled(form.value) : item.editDisabled
}
if (currentAction.value === "see") {
return true
}
if (!isUndefined(item.disabled)) {
return item.disabled
@@ -327,10 +383,13 @@ const formItemDisabled = (item) => {
}
const formItemReadonly = (item) => {
if (currentAction.value === "add" && !isUndefined(item.addReadonly)) {
return item.addReadonly
return isFunction(item.addReadonly) ? item.addReadonly() : item.addReadonly
}
if (currentAction.value === "edit" && !isUndefined(item.editReadonly)) {
return item.editReadonly
return isFunction(item.editReadonly) ? item.editReadonly(form.value) : item.editReadonly
}
if (currentAction.value === "see") {
return true
}
if (!isUndefined(item.readonly)) {
return item.readonly
@@ -371,5 +430,6 @@ const getFormColumns = async (type = "add") => {
await init()
return formColumns.value
}
defineExpose({ add, edit, currentAction, form, getFormColumns, actionTitle })
// 修改源码,暴露actionTitle
defineExpose({ add, edit, see, currentAction, form, getFormColumns, maFormRef, actionTitle })
</script>

View File

@@ -1,11 +1,6 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<a-modal v-model:visible="visible" :footer="false" @cancel="close" draggable>
@@ -37,6 +32,7 @@ import { Message } from "@arco-design/web-vue"
const visible = ref(false)
const options = inject("options")
const emit = defineEmits(["success"])
const open = () => (visible.value = true)
const close = () => (visible.value = false)
@@ -46,8 +42,9 @@ const upload = (fileOption) => {
const dataForm = new FormData()
dataForm.append("file", fileOption.fileItem.file)
commonApi.importExcel(options.import.url, dataForm).then((res) => {
commonApi.importExcel(options.import.url, dataForm).then(async (res) => {
res.success && Message.success(res.message || "导入成功")
emit("success")
close()
})
}

View File

@@ -1,11 +1,6 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<a-spin :loading="searchLoading" :tip="options.searchLoadingText" v-if="showSearch">
@@ -25,7 +20,10 @@
:label-col-style="{ width: component.searchLabelWidth ?? options.searchLabelWidth }"
>
<slot :name="`${component.dataIndex}`" v-bind="{ searchForm, component }">
<component :is="getComponentName(component.formType)" :component="component" />
<component
:is="getComponentName(component.searchFormType ?? component.formType)"
:component="component"
/>
</slot>
</a-form-item>
</template>
@@ -57,7 +55,7 @@ import MaFormPicker from "./searchFormItem/form-picker.vue"
import MaFormSelect from "./searchFormItem/form-select.vue"
import MaFormCascader from "./searchFormItem/form-cascader.vue"
import MaFormTreeSelect from "./searchFormItem/form-tree-select.vue"
import { cloneDeep, isFunction } from "lodash"
import { cloneDeep, isFunction } from "lodash-es"
const options = inject("options")
const columns = inject("columns")
@@ -73,14 +71,30 @@ const searchColumns = ref([])
const searchForm = ref({})
provide("searchForm", searchForm)
provide("columns", columns)
const emit = defineEmits(["search"])
if (columns.length > 0) {
searchColumns.value = cloneDeep(
columns.filter((item) => item.search === true && options.tabs?.dataIndex != item.dataIndex)
)
const getSearchAllColumns = (cls = []) => {
let sls = []
cls.map((item) => {
if (item.children && item.children.length > 0) {
let tmp = getSearchAllColumns(item.children)
sls.push(...tmp)
} else if (item.dataIndex && item.search && item.search === true) {
sls.push(item)
}
})
return sls
}
const initSearchColumns = () => {
if (columns.value.length > 0) {
searchColumns.value = cloneDeep(
getSearchAllColumns(columns.value).filter(
(item) => item.search === true && options.tabs?.dataIndex != item.dataIndex
)
)
}
}
const handlerSearch = () => {
@@ -89,6 +103,13 @@ const handlerSearch = () => {
const resetSearch = async () => {
searchRef.value.resetFields()
Object.keys(searchForm.value).map((item) => {
let temp = item.match(/^(.+)Min$/)
if (temp) {
searchForm.value[temp[1] + "Min"] = undefined
searchForm.value[temp[1] + "Max"] = undefined
}
})
if (options.resetSearch && isFunction(options.resetSearch)) {
await options.resetSearch(searchForm.value)
}
@@ -117,6 +138,8 @@ const getComponentName = (formType) => {
}
}
initSearchColumns()
const setSearchHidden = () => (showSearch.value = false)
const setSearchDisplay = () => (showSearch.value = true)
const setSearchLoading = () => (searchLoading.value = true)
@@ -125,9 +148,11 @@ const getSearchFormRef = () => searchRef.value
const getSearchColumns = () => searchColumns.value
defineExpose({
initSearchColumns,
getSearchFormRef,
getSearchColumns,
showSearch,
resetSearch,
setSearchHidden,
setSearchDisplay,
setSearchLoading,

View File

@@ -1,11 +1,6 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<a-cascader
@@ -21,7 +16,7 @@
<script setup>
import { ref, inject, watch } from "vue"
import { get, set } from "lodash"
import { get, set } from "lodash-es"
const props = defineProps({
component: Object
})

View File

@@ -1,5 +1,30 @@
<!--
- @Author XXX
- @Link XXX
-->
<template>
<a-input-group v-if="props.component.formType === 'input-number' && (props.component?.rangeSearch ?? false)">
<a-input-number
v-model="minData"
allow-clear
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}最小值`"
/>
<span class="p-1">~</span>
<a-input-number
v-model="maxData"
allow-clear
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}最大值`"
/>
</a-input-group>
<a-input-number
v-else-if="props.component.formType === 'input-number'"
v-model="value"
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}`"
allow-clear
/>
<a-input
v-else
v-model="value"
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}`"
allow-clear
@@ -8,14 +33,36 @@
<script setup>
import { ref, inject, watch } from "vue"
import { get, set } from "lodash"
import { get, set, isArray } from "lodash-es"
const props = defineProps({
component: Object
})
const searchForm = inject("searchForm")
const minData = ref(isArray(props.component?.searchDefaultValue) ? props.component?.searchDefaultValue[0] : undefined)
const maxData = ref(isArray(props.component?.searchDefaultValue) ? props.component?.searchDefaultValue[1] : undefined)
const value = ref(
get(
searchForm.value,
props.component.dataIndex,
(props.component.searchDefaultValue ?? props.component.formType === "input-number") ? 0 : ""
)
)
const value = ref(get(searchForm.value, props.component.dataIndex, props.component.searchDefaultValue ?? ""))
set(searchForm.value, props.component.dataIndex, value.value)
if (props.component.formType === "input-number" && (props.component?.rangeSearch ?? false)) {
set(searchForm.value, `${props.component.dataIndex}Min`, minData.value)
set(searchForm.value, `${props.component.dataIndex}Max`, maxData.value)
watch(
() => get(searchForm.value, props.component.dataIndex + "Min"),
(vl) => (minData.value = vl)
)
watch(
() => get(searchForm.value, props.component.dataIndex + "Max"),
(vl) => (maxData.value = vl)
)
} else {
set(searchForm.value, props.component.dataIndex, value.value)
}
watch(
() => get(searchForm.value, props.component.dataIndex),
@@ -25,4 +72,12 @@ watch(
() => value.value,
(v) => set(searchForm.value, props.component.dataIndex, v)
)
watch(
() => minData.value,
(v) => set(searchForm.value, `${props.component.dataIndex}Min`, v)
)
watch(
() => maxData.value,
(v) => set(searchForm.value, `${props.component.dataIndex}Max`, v)
)
</script>

View File

@@ -1,11 +1,6 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<component
@@ -18,6 +13,7 @@
"
:time-picker-props="props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}"
:show-time="props.component.showTime"
:type="props.component.range ? (props.component.formType === 'time' ? 'time-range' : 'range') : ''"
:format="props.component.format || ''"
:mode="props.component.mode"
allow-clear
@@ -26,12 +22,13 @@
</template>
<script setup>
import { ref, inject, watch } from "vue"
import { get, set } from "lodash"
import { inject, computed } from "vue"
import { get, set } from "lodash-es"
const props = defineProps({
component: Object
})
const searchForm = inject("searchForm")
const emit = defineEmits(["update:modelValue"])
const getComponentName = () => {
if (["date", "month", "year", "week", "quarter", "range", "time"].includes(props.component.formType)) {
@@ -39,23 +36,30 @@ const getComponentName = () => {
}
}
let defaultValue
const value = computed({
get() {
let val = get(searchForm.value, props.component.dataIndex)
if (val === undefined) {
if (props.component.formType === "range") {
val = props.component.searchDefaultValue ?? []
} else {
val = props.component.searchDefaultValue ?? ""
}
}
if (props.component.formType === "range") {
defaultValue = props.component.searchDefaultValue ?? []
} else {
defaultValue = props.component.searchDefaultValue ?? ""
}
return val
},
set(newVal) {
if (newVal === undefined) {
if (props.component.formType === "range") {
newVal = []
} else {
newVal = ""
}
}
const value = ref(get(searchForm.value, props.component.dataIndex, defaultValue))
set(searchForm.value, props.component.dataIndex, value.value)
watch(
() => get(searchForm.value, props.component.dataIndex),
(vl) => (value.value = vl)
)
watch(
() => value.value,
(v) => set(searchForm.value, props.component.dataIndex, v)
)
emit("update:modelValue", newVal)
set(searchForm.value, props.component.dataIndex, newVal)
}
})
</script>

View File

@@ -1,12 +1,3 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
-->
<template>
<a-select
v-model="value"
@@ -26,7 +17,7 @@
<script setup>
import { ref, inject, watch } from "vue"
import { handlerCascader } from "@cps/ma-form/js/networkRequest"
import { get, set } from "lodash"
import { get, set } from "lodash-es"
const props = defineProps({
component: Object
})

View File

@@ -1,11 +1,6 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<a-tree-select
@@ -23,7 +18,7 @@
<script setup>
import { ref, inject, watch } from "vue"
import { get, set } from "lodash"
import { get, set } from "lodash-es"
const props = defineProps({
component: Object
})

View File

@@ -1,14 +1,9 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
- @Author XXX
- @Link XXX
-->
<template>
<a-drawer :visible="visible" unmountOnClose :footer="false" :width="900" @cancel="onCancel">
<a-drawer :visible="visible" unmountOnClose :footer="false" :width="950" @cancel="onCancel">
<template #title>设置</template>
<a-space class="mt-3">
@@ -59,7 +54,12 @@
<span v-else> / </span>
</template>
</a-table-column>
<a-table-column title="隐藏" data-index="hide" align="center">
<a-table-column title="搜索隐藏" data-index="hide" align="center">
<template #cell="{ record }"
><a-checkbox v-model="record.search" @change="changeColumn($event, 'search', record.dataIndex)"
/></template>
</a-table-column>
<a-table-column title="表格隐藏" data-index="hide" align="center">
<template #cell="{ record }"
><a-checkbox v-model="record.hide" @change="changeColumn($event, 'hide', record.dataIndex)"
/></template>
@@ -123,16 +123,22 @@
import { ref, inject } from "vue"
const options = inject("options")
let columns = inject("columns")
const columns = inject("columns")
const allowShowColumns = ref([])
const allowShowColumns = columns.filter((item) => {
return !(item?.settingHide ?? false)
})
const emit = defineEmits(["onChangeSearchHide", "onChangeColumnHide"])
const setShowColumns = () => {
allowShowColumns.value = columns.value.filter((item) => {
return !(item?.settingHide ?? false)
})
}
const visible = ref(false)
const bordered = ref("column")
const open = () => {
setShowColumns()
visible.value = true
}
@@ -141,7 +147,7 @@ const onCancel = () => {
}
const changeColumn = (ev, type, name) => {
const column = columns.find((item) => item.dataIndex === name)
const column = columns.value.find((item) => item.dataIndex === name)
switch (type) {
case "order":
if (ev === "page") {
@@ -152,6 +158,12 @@ const changeColumn = (ev, type, name) => {
column.sortable = undefined
}
break
case "hide":
emit("onChangeColumnHide")
break
case "search":
emit("onChangeSearchHide")
break
}
}
@@ -171,7 +183,8 @@ const changeBordered = (v) => {
}
const onTableChange = (_data) => {
columns = _data
columns.value = _data
setShowColumns()
}
defineExpose({ open })

View File

@@ -8,14 +8,22 @@
:type="options.tabs.type"
:hide-content="true"
@change="tabChange"
@tab-click="maEvent.customeEvent(options.tabs, $event, 'onClick')"
@tab-click="runEvent(options.tabs, 'onClick', undefined, $event)"
class="ma-tabs mb-5"
>
<template #extra><slot name="tabExtra"></slot></template>
<a-tab-pane :key="item.value" :title="item.label" v-for="item in options.tabs.data"></a-tab-pane>
<a-tab-pane :key="item.value" :title="item.label" v-for="item in options.tabs.data">
<template #title
><slot :name="'tabTitle-' + item.label">{{ item.label }}</slot></template
>
</a-tab-pane>
</a-tabs>
<ma-search @search="searchSubmitHandler" class="__search-panel" ref="crudSearchRef">
<template v-for="(slot, slotIndex) in searchSlots" :key="slotIndex" #[slot]="{ searchForm, component }">
<template
v-for="(slot, slotIndex) in getSearchSlot()"
:key="slotIndex"
#[slot]="{ searchForm, component }"
>
<slot :name="`search-${slot}`" v-bind="{ searchForm, component }" />
</template>
<template #searchBeforeButtons>
@@ -28,6 +36,7 @@
<slot name="searchAfterButtons"></slot>
</template>
</ma-search>
<div v-if="$slots.middleContent" class="mb-2"><slot name="middleContent"></slot></div>
</div>
<div class="_crud-content">
<div class="operation-tools lg:flex justify-between mb-3" ref="crudOperationRef">
@@ -36,6 +45,8 @@
<slot name="tableButtons">
<a-button
v-if="options.add.show"
v-auth="options.add.auth || []"
v-role="options.add.role || []"
@click="addAction"
type="primary"
class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -43,9 +54,15 @@
<template #icon><icon-plus /></template>{{ options.add.text || "新增" }}
</a-button>
<a-popconfirm content="确定要删除数据吗?" position="bottom" @ok="deletesMultipleAction">
<a-popconfirm
content="确定要删除数据吗?"
position="bottom"
@ok="deletesMultipleAction"
v-if="options.delete.show && isBatch(options.delete) && options.rowSelection"
>
<a-button
v-if="options.delete.show"
v-auth="options.delete.auth || []"
v-role="options.delete.role || []"
type="primary"
status="danger"
class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -55,9 +72,15 @@
</a-button>
</a-popconfirm>
<a-popconfirm content="确定要恢复数据吗?" position="bottom" @ok="recoverysMultipleAction">
<a-popconfirm
content="确定要恢复数据吗?"
position="bottom"
@ok="recoverysMultipleAction"
v-if="options.recovery.show && isRecovery && isBatch(options.delete)"
>
<a-button
v-if="options.recovery.show && isRecovery"
v-auth="options.recovery.auth || []"
v-role="options.recovery.role || []"
type="primary"
status="success"
class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -66,11 +89,21 @@
>
</a-popconfirm>
<a-button v-if="options.import.show" @click="importAction" class="w-full lg:w-auto mt-2 lg:mt-0"
<a-button
v-if="options.import.show"
v-auth="options.import.auth || []"
v-role="options.import.role || []"
@click="importAction"
class="w-full lg:w-auto mt-2 lg:mt-0"
><template #icon><icon-upload /></template>{{ options.import.text || "导入" }}</a-button
>
<a-button v-if="options.export.show" @click="exportAction" class="w-full lg:w-auto mt-2 lg:mt-0"
<a-button
v-if="options.export.show"
v-auth="options.export.auth || []"
v-role="options.export.role || []"
@click="exportAction"
class="w-full lg:w-auto mt-2 lg:mt-0"
><template #icon><icon-download /></template>{{ options.export.text || "导出" }}</a-button
>
@@ -112,8 +145,9 @@
</a-space>
</div>
<div ref="crudContentRef">
<slot name="content" v-bind="tableData">
<slot name="crudContent" v-bind="tableData">
<a-table
v-if="tableIsShow"
v-bind="$attrs"
ref="tableRef"
:key="options.pk"
@@ -123,7 +157,7 @@
:pagination="options.tablePagination"
:stripe="options.stripe"
:bordered="options.bordered"
:rowSelection="options.rowSelection || undefined"
:rowSelection="options.rowSelection ?? undefined"
:row-key="options?.rowSelection?.key ?? options.pk"
:scroll="options.scroll"
:column-resizable="options.resizable"
@@ -131,8 +165,8 @@
:row-class="options.rowClass"
:hide-expand-button-on-empty="options.hideExpandButtonOnEmpty"
:default-expand-all-rows="options.expandAllRows"
:summary="options.customerSummary || __summary || options.showSummary"
@selection-change="setSelecteds"
:summary="(options.customerSummary || options.showSummary) && __summary"
v-model:selectedKeys="selecteds"
@sorter-change="handlerSort"
>
<template #tr="{ record }">
@@ -155,7 +189,7 @@
<ma-column
ref="crudColumnRef"
v-if="reloadColumn"
:columns="props.columns"
:columns="columns"
:isRecovery="isRecovery"
:crudFormRef="crudFormRef"
@refresh="() => refresh()"
@@ -174,7 +208,15 @@
</template>
<template
v-for="(slot, slotIndex) in slots"
v-for="(slot, index) in getTitleSlot(columns)"
#[slot]="{ column }"
:key="index"
>
<slot :name="`${slot}`" v-bind="{ column }" />
</template>
<template
v-for="(slot, slotIndex) in getSlot(columns)"
:key="slotIndex"
#[slot]="{ record, column, rowIndex }"
>
@@ -213,19 +255,24 @@
/>
</div>
<ma-setting ref="crudSettingRef" />
<ma-setting ref="crudSettingRef" @onChangeSearchHide="initSearchColumns()" @onChangeColumnHide="changeColumn" />
<!-- 修改源码透传ma-crud属性给ma-form -->
<ma-form ref="crudFormRef" @success="requestSuccess" v-bind="$attrs">
<template v-for="slot in Object.keys($slots)" #[slot]="component">
<template v-for="(slot, index) in Object.keys($slots)" #[slot]="component" :key="index">
<slot :name="slot" v-bind="component" />
</template>
</ma-form>
<ma-import ref="crudImportRef" />
<ma-import ref="crudImportRef" @success="refresh" />
<ma-context-menu ref="crudContextMenuRef" @execCommand="execContextMenuCommand" />
<a-image-preview :src="imgUrl" v-model:visible="imgVisible" />
<a-image-preview-group
:srcList="imgUrl"
v-model:visible="imgVisible"
v-if="typeof imgUrl === 'object' && imgUrl !== null"
/>
<a-image-preview :src="imgUrl" v-model:visible="imgVisible" v-else />
</a-layout-content>
</template>
@@ -234,7 +281,7 @@ import config from "@/config/crud"
import { ref, watch, provide, nextTick, onMounted, onUnmounted } from "vue"
import defaultOptions from "./js/defaultOptions"
import { loadDict } from "@cps/ma-form/js/networkRequest.js"
import ColumnService from "./js/columnService"
import ColumnService from "@cps/ma-form/js/columnService"
import MaSearch from "./components/search.vue"
import MaForm from "./components/form.vue"
@@ -242,12 +289,14 @@ import MaSetting from "./components/setting.vue"
import MaImport from "./components/import.vue"
import MaColumn from "./components/column.vue"
import MaContextMenu from "./components/contextMenu.vue"
import checkAuth from "@/directives/auth/auth"
import checkRole from "@/directives/role/role"
import { Message } from "@arco-design/web-vue"
import { request } from "@/utils/request"
import tool from "@/utils/tool"
import Print from "@/utils/print"
import { isArray, isFunction, isObject, isUndefined } from "lodash"
import { maEvent } from "@cps/ma-form/js/formItemMixin.js"
import { isArray, isFunction, isObject, isUndefined } from "lodash-es"
import { runEvent } from "@cps/ma-form/js/event.js"
import globalColumn from "@/config/column.js"
import { useFormStore } from "@/store/index"
@@ -267,6 +316,7 @@ const dicts = ref({})
const cascaders = ref([])
const reloadColumn = ref(true)
const tableIsShow = ref(true)
const openPagination = ref(false)
const imgVisible = ref(false)
const imgUrl = ref(import.meta.env.VITE_APP_BASE + "not-image.png")
@@ -305,19 +355,21 @@ const init = async () => {
}
// 收集数据
props.columns.map((item) => {
columns.value.map((item) => {
if (item.cascaderItem && item.cascaderItem.length > 0) {
cascaders.value.push(...item.cascaderItem)
}
})
await props.columns.map(async (item) => {
await columns.value.map(async (item) => {
// 字典
if (!cascaders.value.includes(item.dataIndex) && item.dict) {
await loadDict(dicts.value, item)
}
})
await tabsHandler()
setTimeout(async () => {
await tabsHandler()
}, 500)
}
const dictTrans = (dataIndex, value) => {
@@ -343,14 +395,14 @@ columns.value.map((item, index) => {
item = columns.value[index]
}
!item.width && (item.width = options.value.columnWidth)
!item.minWidth && (item.minWidth = options.value.columnMinWidth)
})
provide("options", options.value)
provide("columns", props.columns)
provide("layout", props.layout)
provide("dicts", dicts.value)
provide("dictColors", dictColors.value)
provide("requestParams", requestParams.value)
provide("columns", columns)
provide("dicts", dicts)
provide("layout", props.layout)
provide("dictTrans", dictTrans)
provide("dictColors", dictColors)
provide("isRecovery", isRecovery)
@@ -365,10 +417,7 @@ watch(
(vl) => (options.value.api = vl)
)
watch(
() => openPagination.value,
() => options.value.pageLayout === "fixed" && settingFixedPage()
)
watch([() => openPagination.value, () => total.value], () => options.value.pageLayout === "fixed" && settingFixedPage())
watch(
() => formStore.crudList[options.value.id],
@@ -378,6 +427,11 @@ watch(
}
)
const showImage = (url) => {
imgUrl.value = url
imgVisible.value = true
}
const getSlot = (cls = []) => {
let sls = []
cls.map((item) => {
@@ -391,14 +445,22 @@ const getSlot = (cls = []) => {
return sls
}
const showImage = (url) => {
imgUrl.value = url
imgVisible.value = true
const getTitleSlot = (cls = []) => {
let sls = []
cls.map((item) => {
if (item.children && item.children.length > 0) {
let tmp = getTitleSlot(item.children)
sls.push(...tmp)
} else if (item.dataIndex) {
sls.push(`tableTitle-${item.dataIndex}`)
}
})
return sls
}
const getSearchSlot = () => {
let sls = []
props.columns.map((item) => {
columns.value.map((item) => {
if (item.search && item.search === true) {
sls.push(item.dataIndex)
}
@@ -406,9 +468,6 @@ const getSearchSlot = () => {
return sls
}
slots.value = getSlot(props.columns)
searchSlots.value = getSearchSlot(props.columns)
const requestData = async () => {
await init()
if (options.value.showIndex && columns.value.length > 0 && columns.value[0].dataIndex !== "__index") {
@@ -456,6 +515,7 @@ const requestHandle = async () => {
loading.value = true
isFunction(options.value.beforeRequest) && options.value.beforeRequest(requestParams.value)
if (isFunction(currentApi.value)) {
// 修改源码添加options.prameters参数
if (options.value.parameters) {
requestParams.value = { ...requestParams.value, ...options.value.parameters }
}
@@ -463,7 +523,6 @@ const requestHandle = async () => {
if (response.rows) {
tableData.value = response.rows
if (response.pageInfo) {
// 这里去找total字段
total.value = response.pageInfo.total
openPagination.value = true
} else {
@@ -473,9 +532,9 @@ const requestHandle = async () => {
tableData.value = response
}
} else {
console.error(`ma-crud errorcrud.api 不是一个 Function.`)
console.error(`ma-crud error您传递的api属性不是一个函数.`)
}
isFunction(options.value.afterRequest) && options.value.afterRequest(tableData.value)
isFunction(options.value.afterRequest) && (tableData.value = options.value.afterRequest(tableData.value))
loading.value = false
}
@@ -497,6 +556,8 @@ const refresh = async () => {
: options.value.api
await requestHandle()
}
selecteds.value = []
tableRef.value.selectAll(false)
}
const searchSubmitHandler = async (formData) => {
@@ -544,7 +605,11 @@ const toggleSearch = async () => {
const settingFixedPage = () => {
const workAreaHeight = document.querySelector(".work-area").offsetHeight
const tableHeight = workAreaHeight - headerHeight.value - (openPagination.value ? 152 : 108)
const tableHeight =
workAreaHeight -
headerHeight.value -
(openPagination.value ? 160 : 116) +
(total.value === 0 && !loading.value ? 44 : 0)
crudContentRef.value.style.height = tableHeight + "px"
}
@@ -592,9 +657,17 @@ const dbClickOpenEdit = (record) => {
Message.error("回收站数据不可编辑")
return
}
if (isArray(options.value.edit.auth) || options.value.edit.auth === undefined) {
for (let index in options.value.edit.auth) {
if (!checkAuth(options.value.edit.auth[index])) {
Message.error("没有编辑数据的权限")
return
}
}
if (options.value.edit.api && isFunction(options.value.edit.api)) {
editAction(record)
if (options.value.edit.api && options.value.edit.show && isFunction(options.value.edit.api)) {
editAction(record)
}
}
}
}
@@ -630,6 +703,7 @@ const deletesMultipleAction = async () => {
options.value.afterDelete(response)
}
response.success && Message.success(response.message || `删除成功!`)
selecteds.value = []
await refresh()
} else {
Message.error("至少选择一条数据")
@@ -640,6 +714,7 @@ const recoverysMultipleAction = async () => {
if (selecteds.value && selecteds.value.length > 0) {
const response = await options.value.recovery.api({ ids: selecteds.value })
response.success && Message.success(response.message || `恢复成功!`)
selecteds.value = []
await refresh()
} else {
Message.error("至少选择一条数据")
@@ -690,24 +765,30 @@ const __summary = ({ data }) => {
let summarySuffixText = {}
let length = data.length || 0
summary.map((item) => {
summaryData[item.dataIndex] = 0
summaryPrefixText[item.dataIndex] = item?.prefixText ?? ""
summarySuffixText[item.dataIndex] = item?.suffixText ?? ""
data.map((record) => {
if (record[item.dataIndex]) {
if (item.action && item.action === "sum") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex])
if (item.action && item.action === "text") {
summaryData[item.dataIndex] = item.content
} else {
summaryData[item.dataIndex] = 0
summaryPrefixText[item.dataIndex] = item?.prefixText ?? ""
summarySuffixText[item.dataIndex] = item?.suffixText ?? ""
data.map((record) => {
if (record[item.dataIndex]) {
if (item.action && item.action === "sum") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex])
}
if (item.action && item.action === "avg") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex]) / length
}
}
if (item.action && item.action === "avg") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex]) / length
}
}
})
})
}
})
for (let i in summaryData) {
summaryData[i] =
summaryPrefixText[i] + tool.groupSeparator(summaryData[i].toFixed(2)) + summarySuffixText[i]
if (/^\d+(\.\d+)?$/.test(summaryData[i])) {
summaryData[i] =
summaryPrefixText[i] + tool.groupSeparator(summaryData[i].toFixed(2)) + summarySuffixText[i]
}
}
return [summaryData]
@@ -724,7 +805,7 @@ const tabChange = async (value) => {
const params = {}
params[searchKey] = value
requestParams.value = Object.assign(requestParams.value, params)
await maEvent.customeEvent(options.value.tabs, value, "onChange")
await runEvent(options.value.tabs, "onChange", undefined, value)
await refresh()
}
@@ -756,7 +837,7 @@ const execContextMenuCommand = async (args) => {
crudColumnRef.value.deleteAction(record)
break
default:
await maEvent.customeEvent(item, args, "onCommand")
await runEvent(item, "onCommand", undefined, args)
break
}
}
@@ -767,26 +848,33 @@ const tabsHandler = async () => {
if (isFunction(tabs.data) || isArray(tabs.data)) {
tabs.data = isFunction(tabs.data) ? await tabs.data() : tabs.data
} else if (!isUndefined(tabs.dataIndex)) {
const col = props.columns.find((item) => item.dataIndex === tabs.dataIndex)
const col = columns.value.find((item) => item.dataIndex === tabs.dataIndex)
if (col.search === true && isObject(col.dict)) {
tabs.data = dicts.value[tabs.dataIndex]
}
}
}
onMounted(async () => {
if (typeof options.value.autoRequest == "undefined" || options.value.autoRequest) {
await requestData()
}
const isBatch = (obj) => (isUndefined(obj) ? true : (obj?.batch ?? true))
const changeColumn = async () => {
tableIsShow.value = false
await nextTick(() => (tableIsShow.value = true))
}
onMounted(async () => {
if (!options.value.expandSearch && crudSearchRef.value) {
crudSearchRef.value.setSearchHidden()
}
if (options.value.pageLayout === "fixed") {
window.addEventListener("resize", resizeHandler, false)
headerHeight.value = crudHeaderRef.value.offsetHeight
settingFixedPage()
await nextTick(() => {
window.addEventListener("resize", resizeHandler, false)
headerHeight.value = crudHeaderRef.value.offsetHeight
settingFixedPage()
})
}
if (typeof options.value.autoRequest == "undefined" || options.value.autoRequest) {
await requestData()
}
})
@@ -803,6 +891,11 @@ const getFormColumns = async (type = "add") => {
return await crudFormRef.value.getFormColumns(type)
}
const getCurrentPage = () => requestParams.value[config.request.page]
const getPageSize = () => requestParams.value[config.request.pageSize]
const getTotal = () => total.value
const initSearchColumns = () => crudSearchRef.value.initSearchColumns()
/**
* 获取column属性服务类
* @returns ColumnService
@@ -810,7 +903,7 @@ const getFormColumns = async (type = "add") => {
const getColumnService = (strictMode = true) => {
return new ColumnService({ columns: columns.value, cascaders: cascaders.value, dicts: dicts.value }, strictMode)
}
const setTableData = (data = []) => (tableData.value = data)
defineExpose({
refresh,
requestData,
@@ -822,13 +915,18 @@ defineExpose({
getFormData,
getFormColumns,
getColumnService,
getCurrentPage,
getPageSize,
getTotal,
requestParams,
isRecovery,
tableRef,
initSearchColumns,
crudFormRef,
crudSearchRef,
crudImportRef,
crudSettingRef
crudSettingRef,
setTableData
})
</script>
@@ -840,6 +938,5 @@ defineExpose({
}
._crud-footer {
z-index: 10;
height: 80px;
}
</style>

View File

@@ -2,8 +2,6 @@ import { loadDict } from "@cps/ma-form/js/networkRequest"
/**
* columnService 列服务处理类
* 首先感谢 @NEKGod 提交的PR此功能原本写在了 Ma-Crud 组件,我特意摘出来,封装成类通过引用来调用
* @author NEKGod, X.Mo <root@imoi.cn>
*/
const objectService = function (item) {

View File

@@ -46,7 +46,7 @@ export default {
// 是否显示总结行
showSummary: false,
// 自定义总结行,要传入函数
customerSummary: false,
customerSummary: undefined,
// 是否显示工具栏
showTools: true,
// 表头是否吸顶
@@ -54,7 +54,8 @@ export default {
// 页面布局方式,支持 normal标准和 fixed固定两种
pageLayout: "normal",
// 默认统一设置列宽度
columnWidth: 100,
columnWidth: 0, // 列宽更新为最小列宽(此处设置为 0 时候,默认最小列宽生效)
columnMinWidth: 100,
// 搜索标签对齐方式
searchLabelAlign: "right",
// 全局搜索标签宽度
@@ -159,7 +160,13 @@ export default {
// 按钮文案
text: "编辑",
// 是否显示
show: false
show: false,
// 数据来源table(表格行数据) | api(通过接口获取数据)
dataSource: "table",
// 数据源API接口
dataSourceApi: undefined,
// 是否禁用,仅表格行内按钮有效
disabled: false
},
delete: {
// 删除api
@@ -170,6 +177,8 @@ export default {
role: [],
// 按钮文案
text: "删除",
// 是否禁用,仅表格行内按钮有效
disabled: false,
// 真实删除api
realApi: undefined,
@@ -179,9 +188,13 @@ export default {
realRole: [],
// 真实按钮文案
realText: "删除",
// 真实删除是否禁用,仅表格行内按钮有效
realDisabled: false,
// 是否显示
show: false
show: false,
// 是否显示批量处理按钮
batch: true
},
recovery: {
// 恢复api
@@ -193,18 +206,26 @@ export default {
// 按钮文案
text: "恢复",
// 是否显示
show: false
show: false,
// 是否显示批量处理按钮
batch: true
},
see: {
// 显示查看按钮的权限
auth: [],
// 显示查看按钮的角色
role: [],
// 按钮文案
text: "查看",
// 是否显示
show: false,
// 数据来源table(表格行数据) | api(通过接口获取数据)
dataSource: "table",
// 数据源API接口
dataSourceApi: undefined,
// 是否禁用,仅表格行内按钮有效
disabled: false
},
// see: {
// // 显示查看按钮的权限
// auth: [],
// // 显示查看按钮的角色
// role: [],
// // 按钮文案
// text: '查看',
// // 是否显示
// show: false,
// },
import: {
// 导入url
url: undefined,

View File

@@ -1,116 +1,133 @@
import { VNodeChild } from "vue"
import { VNodeChild,Ref } from "vue";
/**
* 表单组件类型
*/
import { FieldRule } from "@arco-design/web-vue"
import { FieldRule } from "@arco-design/web-vue";
export type FormDataType =
| "radio"
| "checkbox"
| "select"
| "transfer"
| "tree-select"
| "cascader"
| "date"
| "month"
| "year"
| "week"
| "quarter"
| "range"
| "time"
| "input"
| "password"
| "textarea"
| "upload"
| "select-user"
| "editor"
| "icon"
| "user-info"
| "city-linkage"
| "form-group"
| "select-resource"
| 'component';
export type FormDateType =
| "radio"
| "checkbox"
| "select"
| "transfer"
| "tree-select"
| "cascader"
| "date"
| "month"
| "year"
| "week"
| "quarter"
| "range"
| "time"
| "input"
| "password"
| "textarea"
| "upload"
| "select-user"
| "editor"
| "code-editor"
| "icon"
| "user-info"
| "city-linkage"
| "form-group"
| "select-resource"
/**
* 列字典
*/
export interface ColumnDict {
// 字典名称,快捷查询字典接口查询
name?: string
// 自定义url查询
url?: string
// url查询方法,填写url之后生效
method?: "GET" | "POST" | "PUT" | "DELETE"
// url查询params数据,填写url之后生效
params?: object
// url查询body数据,填写url之后生效
body?: object
// 直接设置字典值
data?: object | Function
// 表格列的值是否翻译为字典对应标签
translation?: boolean
// 表格key 和 value的props设置
props?: {
label?: string
value?: string
}
// 字典名称,快捷查询字典接口查询
name?: string;
// 自定义url查询
url?: string;
// url查询方法,填写url之后生效
method?: "GET" | "POST" | "PUT" | "DELETE";
// url查询params数据,填写url之后生效
params?: object;
// url查询body数据,填写url之后生效
body?: object;
// 直接设置字典值
data?: object | Function;
// 表格列的值是否翻译为字典对应标签
translation?: boolean;
// 表格key 和 value的props设置
props?: {
label?: string;
value?: string;
};
}
export interface BasicColumn {
// 标题
title: string
// 字段名称
dataIndex: string
// 组件类型
formType?: FormDateType
// 表格列对齐方式
align?: "center" | "right" | "left"
// 字段是否加入搜索
search?: boolean
// 列宽
width?: number | "auto"
// 表格列是否隐藏
hide?: boolean
// 编辑|创建 通用是否显示字段
display?: boolean
// 添加弹窗是否显示字段
addDisplay?: boolean
// 编辑弹窗是否显示字段
editDisplay?: boolean
// 编辑|创建 通用是否禁用字段
disabled?: boolean
// 添加弹窗是否禁用字段
addDisabled?: boolean
// 编辑弹窗是否禁用字段
editDisabled?: boolean
// 编辑|创建 通用是否只读字段
readonly?: boolean
// 添加弹窗是否只读字段
addReadonly?: boolean
// 编辑弹窗是否只读字段
editReadonly?: boolean
// 自定义渲染
customRender?: (({ record, column, rowIndex }) => VNodeChild | JSX.Element) | VNodeChild | JSX.Element
// 字段新增时默认值
addDefaultValue?: number | string | boolean | undefined | ((record) => number | string | boolean | undefined)
// 字段编辑时默认值
editDefaultValue?: number | string | boolean | undefined | ((record) => number | string | boolean | undefined)
// select,radio,treeSelect,下拉字典配置
dict?: ColumnDict
// 继承公用配置
common?: boolean
// select 和 tree-select 组件是否开启虚拟列表
virtualList?: boolean
// 搜索默认值
searchDefaultValue?: number | string | undefined
// 搜索描述
searchPlaceholder?: string
//编辑|创建 通用规则
commonRules?: FieldRule | FieldRule[]
// 创建时规则
addRules?: FieldRule | FieldRule[]
// 编辑时规则
editRules?: FieldRule | FieldRule[]
// 子表单数据
children?: BasicColumn[]
// 标题
title: string;
// 字段名称
dataIndex: string;
// 组件类型
formType?: FormDataType;
// 表格列对齐方式
align?: "center" | "right" | "left";
// 字段是否加入搜索
search?: boolean;
// 列宽
width?: number | "auto";
// 表格列是否隐藏
hide?: boolean;
// 编辑|创建 通用是否显示字段
display?: boolean;
// 添加弹窗是否显示字段
addDisplay?: boolean | (() => boolean);
// 编辑弹窗是否显示字段
editDisplay?: boolean | ((record) => boolean);
// 编辑|创建 通用是否禁用字段
disabled?: boolean;
// 添加弹窗是否禁用字段
addDisabled?: boolean | (() => boolean);
// 编辑弹窗是否禁用字段
editDisabled?: boolean | ((record) => boolean);
// 编辑|创建 通用是否只读字段
readonly?: boolean;
// 添加弹窗是否只读字段
addReadonly?: boolean | (() => boolean);
// 编辑弹窗是否只读字段
editReadonly?: boolean | ((record) => boolean);
// 自定义渲染
customRender?:
| (({ record, column, rowIndex }) => VNodeChild | JSX.Element)
| VNodeChild
| JSX.Element;
// 字段新增时默认值
addDefaultValue?:
| number
| string
| boolean
| undefined
| ((record) => number | string | boolean | undefined);
// 字段编辑时默认值
editDefaultValue?:
| number
| string
| boolean
| undefined
| ((record) => number | string | boolean | undefined);
// select,radio,treeSelect,下拉字典配置
dict?: ColumnDict;
// 继承公用配置
common?: boolean;
// select 和 tree-select 组件是否开启虚拟列表
virtualList?: boolean;
// 搜索默认值
searchDefaultValue?: number | string | undefined;
// 搜索描述
searchPlaceholder?: string;
// 表格是否快捷编辑,只支持 input date select
quickEdit?: boolean;
component?:Ref;
//编辑|创建 通用规则
commonRules?: FieldRule | FieldRule[];
// 创建时规则
addRules?: FieldRule | FieldRule[];
// 编辑时规则
editRules?: FieldRule | FieldRule[];
// 子表单数据
children?: BasicColumn[];
}

View File

@@ -1,189 +1,189 @@
export interface BasicCrud {
// 表格接口
api?: undefined | any
// 主键名称
pk?: string
// 设置选择列
rowSelection?:
| undefined
| {
// 选择值的标识默认id
key?: string
// 选择列是否显示全选
showCheckedAll?: boolean
// 行选择器类型
type?: "checkbox" | "radio"
// 选择器列标题
title?: string | "#"
// 列宽度
width?: number | 60
// 是否固定
fixed?: boolean | false
// 是否仅展示当前页的keys
onlyCurrent?: boolean | true
}
// 搜索label宽度
searchLabelWidth?: string | "auto"
// 搜索label对齐方式
searchLabelAlign?: "center" | "right" | "left"
// 一行多少列
searchLabelCols?: number
// 是否显示边框
bordered?: { wrapper?: boolean; cell?: boolean }
// 是否开启拖拽排序
dragSort?: boolean
// 子节点为空隐藏节点按钮
hideExpandButtonOnEmpty?: boolean
// 默认展开所有行
expandAllRows?: boolean
// 斑马线
stripe?: boolean
// 新增、编辑、删除完成后是否刷新表格
dataCompleteRefresh?: boolean
// 表格大小
size?: "mini" | "small" | "medium" | "large"
// 是否开启双击编辑数据
isDbClickEdit?: boolean
// 是否显示展开/折叠按钮
isExpand?: boolean
// 是否显示自定义
showExpandRow?: boolean
// 是否显示总结行
showSummary?: boolean
// 自定义总结行,要传入函数
customerSummary?: boolean
// 是否显示工具栏
showTools?: boolean
// 新增和编辑显示设置
formOption?: {
// 显示方式支持模态框和抽屉?: modal drawer
viewType?: "modal" | "drawer"
// 显示宽度
width?: number
// 是否全屏只有modal有效
isFull?: boolean
}
//新增确定之前修改form值
beforeAdd?: (form) => void
//新增确定之后调用,返回接口response和form值
afterAdd?: (response, form) => void
//编辑确定之前修改form值
beforeEdit?: (form) => void
//编辑确定之后调用,返回接口response和form值
afterEdit?: (response, form) => void
add?: {
// 新增api
api?: undefined | any
// 显示新增按钮的权限
auth?: string[]
// 显示新增按钮的角色
role?: string[]
// 按钮文案
text?: string
// 是否显示
show?: boolean
}
edit?: {
// 编辑api
api?: undefined | any
// 显示编辑按钮的权限
auth?: string[]
// 显示编辑按钮的角色
role?: string[]
// 按钮文案
text?: string
// 是否显示
show?: boolean
}
delete?: {
// 删除api
api?: undefined | any
// 显示删除按钮的权限
auth?: string[]
// 显示删除按钮的角色
role?: string[]
// 按钮文案
text?: string
// 表格接口
api?: undefined | any;
// 主键名称
pk?: string;
// 设置选择列
rowSelection?:
| undefined
| {
// 选择值的标识默认id
key?: string;
// 选择列是否显示全选
showCheckedAll?: boolean;
// 行选择器类型
type?: 'checkbox' | 'radio';
// 选择器列标题
title?: string | '#';
// 列宽度
width?: number | 60;
// 是否固定
fixed?: boolean | false;
// 是否仅展示当前页的keys
onlyCurrent?: boolean | true;
};
// 搜索label宽度
searchLabelWidth?: string | "auto";
// 搜索label对齐方式
searchLabelAlign?: "center" | "right" | "left";
// 一行多少列
searchLabelCols?: number;
// 是否显示边框
bordered?: { wrapper?: boolean; cell?: boolean };
// 是否开启拖拽排序
dragSort?: boolean;
// 子节点为空隐藏节点按钮
hideExpandButtonOnEmpty?: boolean;
// 默认展开所有行
expandAllRows?: boolean;
// 斑马线
stripe?: boolean;
// 新增、编辑、删除完成后是否刷新表格
dataCompleteRefresh?: boolean;
// 表格大小
size?: "mini" | "small" | "medium" | "large";
// 是否开启双击编辑数据
isDbClickEdit?: boolean;
// 是否显示展开/折叠按钮
isExpand?: boolean;
// 是否显示自定义
showExpandRow?: boolean;
// 是否显示总结行
showSummary?: boolean;
// 自定义总结行,要传入函数
customerSummary?: boolean;
// 是否显示工具栏
showTools?: boolean;
// 新增和编辑显示设置
formOption?: {
// 显示方式支持模态框和抽屉?: modal drawer
viewType?: "modal" | "drawer";
// 显示宽度
width?: number;
// 是否全屏只有modal有效
isFull?: boolean;
};
//新增确定之前修改form值
beforeAdd?: (form) => void;
//新增确定之后调用,返回接口response和form值
afterAdd?: (response, form) => void;
//编辑确定之前修改form值
beforeEdit?: (form) => void;
//编辑确定之后调用,返回接口response和form值
afterEdit?: (response, form) => void;
add?: {
// 新增api
api?: undefined | any;
// 显示新增按钮的权限
auth?: string[];
// 显示新增按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 是否显示
show?: boolean;
};
edit?: {
// 编辑api
api?: undefined | any;
// 显示编辑按钮的权限
auth?: string[];
// 显示编辑按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 是否显示
show?: boolean;
};
delete?: {
// 删除api
api?: undefined | any;
// 显示删除按钮的权限
auth?: string[];
// 显示删除按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 真实删除api
realApi?: undefined | any
// 显示真实删除按钮的权限
realAuth?: string[]
// 显示真实删除按钮的角色
realRole?: string[]
// 真实按钮文案
realText?: string
// 是否显示
show?: boolean
}
// Todo
recycleApi?: any
recovery?: {
// 显示恢复按钮的权限
auth?: string[]
// 显示恢复按钮的角色
role?: string[]
// 按钮文案
text?: string
// 是否显示
show?: boolean
// 恢复列表查询api
api?: undefined | any
}
// see?: {
// // 显示查看按钮的权限
// auth?: string[]
// // 显示查看按钮的角色
// role?: string[]
// // 按钮文案
// text?: string
// // 是否显示
// show?: boolean
// }
import?: {
// 导入url
url?: undefined | any
// 下载模板地址
templateUrl?: undefined | any
// 显示导入按钮的权限
auth?: string[]
// 显示导入按钮的角色
role?: string[]
// 按钮文案
text?: string
// 是否显示
show?: boolean
}
export?: {
// 导出url
url?: undefined | any
// 显示导出按钮的权限
auth?: string[]
// 显示导出按钮的角色
role?: string[]
// 按钮文案
text?: string
// 是否显示
show?: boolean
}
// 是否显示索引列
showIndex?: boolean
// 索引列名称
indexLabel?: string
// 设置请求数据label
requestParamsLabel?: undefined
// 表格滚动默认宽高
scroll?: {
x?: string
y?: string
}
// 调整列宽
resizable?: boolean
// 是否显示操作列
operationColumn?: boolean
// 操作列宽度
operationWidth?: number
// 操作列名称
operationColumnText?: string
// 真实删除api
realApi?: undefined | any;
// 显示真实删除按钮的权限
realAuth?: string[];
// 显示真实删除按钮的角色
realRole?: string[];
// 真实按钮文案
realText?: string;
// 是否显示
show?: boolean;
};
// Todo
recycleApi?: any;
recovery?: {
// 显示恢复按钮的权限
auth?: string[];
// 显示恢复按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 是否显示
show?: boolean;
// 恢复列表查询api
api?: undefined | any;
};
// see?: {
// // 显示查看按钮的权限
// auth?: string[]
// // 显示查看按钮的角色
// role?: string[]
// // 按钮文案
// text?: string
// // 是否显示
// show?: boolean
// }
import?: {
// 导入url
url?: undefined | any;
// 下载模板地址
templateUrl?: undefined | any;
// 显示导入按钮的权限
auth?: string[];
// 显示导入按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 是否显示
show?: boolean;
};
export?: {
// 导出url
url?: undefined | any;
// 显示导出按钮的权限
auth?: string[];
// 显示导出按钮的角色
role?: string[];
// 按钮文案
text?: string;
// 是否显示
show?: boolean;
};
// 是否显示索引列
showIndex?: boolean;
// 索引列名称
indexLabel?: string;
// 设置请求数据label
requestParamsLabel?: undefined;
// 表格滚动默认宽高
scroll?: {
x?: string;
y?: string;
};
// 调整列宽
resizable?: boolean;
// 是否显示操作列
operationColumn?: boolean;
// 操作列宽度
operationWidth?: number;
// 操作列名称
operationColumnText?: string;
}