40 changed files with 5227 additions and 64 deletions
@ -1,9 +1,94 @@ |
|||
<template> |
|||
<div>{{ message }}</div> |
|||
<div> |
|||
<PlatformGrid |
|||
ref="dataSupplementTypeGridRef" |
|||
:query-form-cols-number="dataSupplementTypeGrid.queryFormColsNumber" |
|||
:query-form-cols-auto="dataSupplementTypeGrid.queryFormColsAuto" |
|||
:table-title="dataSupplementTypeGrid.tableTitle" |
|||
:table-row-key="dataSupplementTypeGrid.tableRowKey" |
|||
:table-init-load-data="dataSupplementTypeGrid.tableInitLoadData" |
|||
:table-data-url="dataSupplementTypeGrid.tableDataUrl" |
|||
:table-row-drag="true" |
|||
:table-show-sort-no="dataSupplementTypeGrid.tableShowSortNo" |
|||
:table-columns="dataSupplementTypeGrid.tableColumns" |
|||
:table-left-column-sticky-number="dataSupplementTypeGrid.tableLeftColumnStickyNumber" |
|||
:table-buttons="dataSupplementTypeGrid.tableButtons" |
|||
:query-form-fields="dataSupplementTypeGrid.queryFormFields" |
|||
:table-pagination="dataSupplementTypeGrid.tablePagination" |
|||
:add-form-props="dataSupplementTypeGrid.addFormProps" |
|||
></PlatformGrid> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { Environment, axios } from 'platform-core'; |
|||
import { ref } from 'vue'; |
|||
import { Environment } from 'platform-core'; |
|||
import { PlatformGrid } from '@/platform'; |
|||
import { PlatformIconEnum } from '@/platform/utils'; |
|||
|
|||
const dataSupplementTypeGridRef = ref(); |
|||
const dataSupplementContentDialogRef = ref(); |
|||
|
|||
const response = await axios.post(Environment.apiContextPath('/api/sample/action2')); |
|||
const message = response.data.message; |
|||
const dataSupplementTypeGrid = { |
|||
queryFormColsNumber: 2, |
|||
queryFormColsAuto: false, |
|||
queryFormFields: [ |
|||
{ label: '数据补录类型名称', modelName: 'typeName', type: 'text' }, |
|||
{ label: '数据补录类型说明', modelName: 'typeDesc', type: 'text' }, |
|||
], |
|||
hideBottom: false, |
|||
tableInitLoadData: true, |
|||
tableLeftColumnStickyNumber: 0, |
|||
tableTitle: '数据补录类型列表', |
|||
tableRowKey: 'id', |
|||
tableDataUrl: Environment.apiContextPath('api/data/supplement/type'), |
|||
tablePagination: { |
|||
sortBy: 'lastModifyDate', |
|||
descending: true, |
|||
reqPageStart: 0, |
|||
rowsPerPage: 10, |
|||
}, |
|||
tableButtons: [ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
'add', |
|||
'edit', |
|||
'delete', |
|||
'separator', |
|||
{ |
|||
icon: PlatformIconEnum.扳手, |
|||
label: '补录内容管理', |
|||
enableIf: (tableSelected) => { |
|||
if (tableSelected && tableSelected.length > 0) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
click: (tableSelected) => { |
|||
dataSupplementContentDialogRef.value.dialogShow(tableSelected[0]); |
|||
}, |
|||
}, |
|||
'separator', |
|||
'inFullscreen', |
|||
], |
|||
tableShowSortNo: true, |
|||
tableColumns: [ |
|||
{ name: 'typeName', label: '数据补录类型名称' }, |
|||
{ name: 'typeDesc', label: '数据补录类型说明' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
addFormProps: { |
|||
dialogInitWidth: '60%', |
|||
dialogInitHeight: '50%', |
|||
formColsNumber: 1, |
|||
formColsAuto: false, |
|||
formFields: [ |
|||
{ label: '数据补录类型名称', modelName: 'typeName', type: 'text', required: true }, |
|||
{ label: '数据补录类型说明', modelName: 'typeDesc', type: 'textarea' }, |
|||
], |
|||
}, |
|||
}; |
|||
</script> |
|||
|
@ -0,0 +1,93 @@ |
|||
<template> |
|||
<div> |
|||
<q-dialog v-model="drawer.show" position="right" v-bind="attrs" :maximized="true"> |
|||
<q-card :style="drawerStyleComputed"> |
|||
<div class="w-full h-full"> |
|||
<div style="height: 69px"> |
|||
<q-card-section> |
|||
<div class="flex justify-between"> |
|||
<div class="text-h6">{{ title }}</div> |
|||
<div class="flex justify-end gap-4"> |
|||
<template v-if="buttons && buttons.length > 0"> |
|||
<template v-for="(btn, index) in buttons as any" :key="index"> |
|||
<q-btn v-if="typeof btn === 'object'" :loading="false" :color="'primary'" v-bind="btn" @click="btn.click ? btn.click() : () => {}"> |
|||
</q-btn> |
|||
</template> |
|||
</template> |
|||
<slot name="buttons"></slot> |
|||
<q-btn |
|||
v-if="canMaximized" |
|||
dense |
|||
flat |
|||
:icon="!drawer.actualMaximizedToggle ? IconEnum.全屏 : IconEnum.退出全屏" |
|||
@click="drawerMaximizeBtnClick" |
|||
> |
|||
<q-tooltip v-if="!drawer.actualMaximizedToggle">全屏</q-tooltip> |
|||
<q-tooltip v-else-if="drawer.actualMaximizedToggle">退出全屏</q-tooltip> |
|||
</q-btn> |
|||
<q-btn v-close-popup dense flat :icon="IconEnum.关闭"> |
|||
<q-tooltip>关闭</q-tooltip> |
|||
</q-btn> |
|||
</div> |
|||
</div> |
|||
</q-card-section> |
|||
<q-separator /> |
|||
</div> |
|||
<div style="height: calc(100% - 69px)"> |
|||
<q-card-section style="height: 100%; padding: 0px" class="scroll"> |
|||
<slot></slot> |
|||
</q-card-section> |
|||
</div> |
|||
</div> |
|||
</q-card> |
|||
</q-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { reactive, computed, useAttrs } from 'vue'; |
|||
import { IconEnum } from '@/platform/enums'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const props = defineProps({ |
|||
title: { type: String, default: '' }, |
|||
width: { type: String, default: '50vw' }, |
|||
height: { type: String, default: '100vh' }, |
|||
canMaximized: { type: Boolean, default: true }, |
|||
buttons: { type: Array, default: () => [] }, |
|||
}); |
|||
|
|||
const drawer = reactive({ |
|||
show: false, |
|||
/** |
|||
* 实际全屏属性,Drawer 使用的 dialog 进行封装,而 dialog 的非全屏模式, |
|||
* 不管高宽给到多少,Quasar 内置的 css 样式都会有一个 padding: 24px 的属性, |
|||
* 所以封装 Drawer 时默认就是 dialog 的全屏模式,同时希望提供全屏的功能,所以需要一个实际是否全屏的属性。 |
|||
*/ |
|||
actualMaximizedToggle: false, |
|||
}); |
|||
|
|||
const drawerMaximizeBtnClick = () => { |
|||
drawer.actualMaximizedToggle = !drawer.actualMaximizedToggle; |
|||
}; |
|||
|
|||
const drawerStyleComputed = computed(() => { |
|||
if (!drawer.actualMaximizedToggle) { |
|||
return { width: props.width, 'max-width': '100vw', height: props.height, 'max-height': '100vh' }; |
|||
} else { |
|||
return { width: '100vw', 'max-width': '100vw', height: '100vh', 'max-height': '100vh' }; |
|||
} |
|||
}); |
|||
|
|||
const show = () => { |
|||
drawer.show = true; |
|||
}; |
|||
const hide = () => { |
|||
drawer.show = false; |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,197 @@ |
|||
<template> |
|||
<div> |
|||
<q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs"> |
|||
<div class="grid" :class="formLayoutComputed"> |
|||
<template v-for="(field, index) in fields as any" :key="String(index)"> |
|||
<div |
|||
:class=" |
|||
(field.colsFirst ? 'col-start-1 ' : ' ') + |
|||
(field.colspan === 'full' |
|||
? ' col-span-' + screenColsNumComputed |
|||
: field.colspan && screenColsNumComputed >= field.colspan |
|||
? ' col-span-' + field.colspan |
|||
: ' col-span-1') |
|||
" |
|||
> |
|||
<component :is="field.type" v-if="field.name" v-model="formData[field.name]" v-bind="field" :form-data="formData"></component> |
|||
<component :is="field.type" v-else :form-ref="formRef" v-bind="field" :form-data="formData"></component> |
|||
</div> |
|||
</template> |
|||
</div> |
|||
<slot></slot> |
|||
</q-form> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive, watch, computed, toRaw, defineProps, useAttrs } from 'vue'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { PageStatusEnum } from '@/platform/components/utils'; |
|||
|
|||
const $q = useQuasar(); |
|||
const attrs = useAttrs(); |
|||
const props = defineProps({ |
|||
colsNum: { type: Number, default: 0 }, |
|||
// 不同屏幕尺寸下 colsNum 为 0 时一行显示的字段个数 |
|||
screenCols: { |
|||
type: Object, |
|||
default: () => { |
|||
return { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 }; |
|||
}, |
|||
}, |
|||
colsXGap: { type: Number, default: 8 }, |
|||
colsYGap: { type: Number, default: 4 }, |
|||
fields: { |
|||
type: Array, |
|||
default: () => { |
|||
return []; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const formRef = ref(); |
|||
const formStatus = ref(PageStatusEnum.新增); |
|||
const formModel: any = {}; |
|||
const formFieldsMap = new Map(); |
|||
|
|||
const defaultValueHandler = (field) => { |
|||
if (field.hasOwnProperty('defaultValue') && field.defaultValue !== null && field.defaultValue !== undefined) { |
|||
return field.defaultValue; |
|||
} else if (field.type === 'w-checkbox') { |
|||
return false; |
|||
} |
|||
return null; |
|||
}; |
|||
|
|||
watch( |
|||
() => props.fields, |
|||
(newVal, oldVal) => { |
|||
if (newVal) { |
|||
for (const field of props.fields as any) { |
|||
if (field.name) { |
|||
formModel[field.name] = defaultValueHandler(field); |
|||
formFieldsMap.set(field.name, field); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
); |
|||
|
|||
for (const field of props.fields as any) { |
|||
if (field.name) { |
|||
formModel[field.name] = defaultValueHandler(field); |
|||
formFieldsMap.set(field.name, field); |
|||
} |
|||
} |
|||
|
|||
const formData = reactive(formModel); |
|||
|
|||
const screenColsNumComputed = computed(() => { |
|||
if (props.colsNum > 0) { |
|||
return props.colsNum; |
|||
} else { |
|||
return props.screenCols[$q.screen.name]; |
|||
} |
|||
}); |
|||
const formLayoutComputed = computed(() => { |
|||
let className = ''; |
|||
if (props.colsNum > 0) { |
|||
className = 'grid-cols-' + props.colsNum; |
|||
} else { |
|||
className = 'grid-cols-' + screenColsNumComputed.value; |
|||
} |
|||
className += ' gap-x-[' + props.colsXGap + 'px]'; |
|||
className += ' gap-y-[' + props.colsYGap + 'px]'; |
|||
return className; |
|||
}); |
|||
|
|||
/** |
|||
* 对外暴露方法-获取form所有数据 |
|||
*/ |
|||
const getData = () => { |
|||
const data = { ...toRaw(formData) }; |
|||
return data; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-设置form所有字段值 |
|||
* @param data 数据对象(JSON格式) |
|||
*/ |
|||
const setData = (data) => { |
|||
for (const field of props.fields as any) { |
|||
formData[field.name] = data[field.name]; |
|||
} |
|||
}; |
|||
/** |
|||
* 对外暴露方法-重置表单 |
|||
*/ |
|||
const reset = () => { |
|||
Object.keys(formData).forEach((key) => { |
|||
formData[key] = defaultValueHandler(formFieldsMap.get(key)); |
|||
}); |
|||
}; |
|||
const formValidate = async () => { |
|||
let validate = false; |
|||
await formRef.value.validate().then((success) => { |
|||
if (success) { |
|||
validate = true; |
|||
} |
|||
}); |
|||
return validate; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-表单验证 |
|||
*/ |
|||
const validate = async () => { |
|||
const v = await formValidate(); |
|||
return v; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-设置字段值 |
|||
* @param fieldName 字段name |
|||
* @param value 字段值 |
|||
*/ |
|||
const setFieldValue = (fieldName, value) => { |
|||
const field = formFieldsMap.get(fieldName); |
|||
formData[field.name] = value; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-获取字段值 |
|||
* @param fieldName 字段name |
|||
*/ |
|||
const getFieldValue = (fieldName) => { |
|||
return formData[fieldName]; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-设置form状态 |
|||
* @param status 状态 |
|||
*/ |
|||
const setStatus = (status) => { |
|||
formStatus.value = status; |
|||
}; |
|||
/** |
|||
* 对外暴露方法-获取form状态 |
|||
*/ |
|||
const getStatus = () => { |
|||
return toRaw(formStatus.value); |
|||
}; |
|||
/** |
|||
* 对外暴露方法-获取当前一行应该显示的元素个数 |
|||
*/ |
|||
const getRowColsNum = () => { |
|||
return screenColsNumComputed.value; |
|||
}; |
|||
|
|||
defineExpose({ |
|||
data: formData, |
|||
fieldsMap: formFieldsMap, |
|||
getData, |
|||
setData, |
|||
reset, |
|||
validate, |
|||
getFieldValue, |
|||
setFieldValue, |
|||
setStatus, |
|||
getStatus, |
|||
getRowColsNum, |
|||
}); |
|||
</script> |
@ -0,0 +1,39 @@ |
|||
<template> |
|||
<div> |
|||
<q-checkbox v-show="!hideIfComputed" v-bind="attrs" :rules="rulesComputed" :disable="disableIfComputed"></q-checkbox> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
// import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,94 @@ |
|||
<template> |
|||
<div> |
|||
<q-input |
|||
v-show="!hideIfComputed" |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
<template #append> |
|||
<q-icon :name="IconEnum.日期" class="cursor-pointer"> |
|||
<q-popup-proxy cover transition-show="scale" transition-hide="scale"> |
|||
<q-date v-model="date" today-btn mask="YYYY-MM-DD"> |
|||
<div class="row items-center justify-end"> |
|||
<q-btn v-close-popup label="关闭" color="primary" flat /> |
|||
</div> |
|||
</q-date> |
|||
</q-popup-proxy> |
|||
</q-icon> |
|||
</template> |
|||
</q-input> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, computed, defineProps, useAttrs, watch } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
import { IconEnum } from '@/platform/enums'; |
|||
|
|||
const date = ref(null); |
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
watch( |
|||
() => date.value, |
|||
(newVal, oldVal) => { |
|||
attrs['form-data'][attrs.name] = newVal; |
|||
}, |
|||
); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,78 @@ |
|||
<template> |
|||
<div> |
|||
<q-input |
|||
v-show="!hideIfComputed" |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
type="number" |
|||
title="" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
</q-input> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
precision: { type: Number, default: 0 }, |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
rules.push(FormValidators.maxPrecision(props.precision)); |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,76 @@ |
|||
<template> |
|||
<div> |
|||
<q-select |
|||
v-show="!hideIfComputed" |
|||
emit-value |
|||
map-options |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
</q-select> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,75 @@ |
|||
<template> |
|||
<div> |
|||
<q-input |
|||
v-show="!hideIfComputed" |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
type="text" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
</q-input> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,87 @@ |
|||
<template> |
|||
<div> |
|||
<q-input |
|||
v-show="!hideIfComputed" |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
type="text" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
<template v-if="attrs.button && attrs.buttonPosition === 'prepend'" #prepend> |
|||
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" /> |
|||
</template> |
|||
<template v-else-if="attrs.button && attrs.buttonPosition === 'before'" #before> |
|||
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" /> |
|||
</template> |
|||
<template v-else-if="attrs.button && attrs.buttonPosition === 'after'" #after> |
|||
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" /> |
|||
</template> |
|||
<template v-else-if="attrs.button" #append> |
|||
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" /> |
|||
</template> |
|||
</q-input> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return true; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,75 @@ |
|||
<template> |
|||
<div> |
|||
<q-input |
|||
v-show="!hideIfComputed" |
|||
:hide-bottom-space="true" |
|||
:hide-hint="true" |
|||
:outlined="true" |
|||
:dense="true" |
|||
v-bind="attrs" |
|||
type="textarea" |
|||
:rules="rulesComputed" |
|||
:readonly="readonlyIfComputed" |
|||
:disable="disableIfComputed" |
|||
> |
|||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|||
</q-input> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, defineProps, useAttrs } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const inRules = attrs.rules; |
|||
const props = defineProps({ |
|||
hideIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
requiredIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
readonlyIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
disableIf: { |
|||
type: Function, |
|||
default: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const rulesComputed = computed(() => { |
|||
let rules = inRules || <any>[]; |
|||
if (!hideIfComputed.value && requiredIfComputed.value) { |
|||
rules.push(FormValidators.required()); |
|||
} else { |
|||
rules.splice(rules.length - 1, 1); |
|||
} |
|||
return rules; |
|||
}); |
|||
|
|||
const hideIfComputed = computed(() => { |
|||
return props.hideIf(); |
|||
}); |
|||
const requiredIfComputed = computed(() => { |
|||
return props.requiredIf(); |
|||
}); |
|||
const readonlyIfComputed = computed(() => { |
|||
return props.readonlyIf(); |
|||
}); |
|||
const disableIfComputed = computed(() => { |
|||
return props.disableIf(); |
|||
}); |
|||
</script> |
@ -0,0 +1,15 @@ |
|||
<template> |
|||
<div> |
|||
<q-icon v-if="value" :name="IconEnum.是状态" color="green" size="sm"> </q-icon> |
|||
<q-icon v-else-if="!value && showNoEnable" :name="IconEnum.否状态" color="red" size="sm"> </q-icon> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { IconEnum } from '@/platform/enums'; |
|||
|
|||
const props = defineProps({ |
|||
value: { type: Boolean, default: false }, // 值 |
|||
showNoEnable: { type: Boolean, default: false }, // 不可用是否显示 |
|||
}); |
|||
</script> |
File diff suppressed because it is too large
@ -0,0 +1,95 @@ |
|||
<template> |
|||
<div> |
|||
<q-markup-table separator="cell" flat bordered wrap-cells v-bind="attrs"> |
|||
<tbody> |
|||
<template v-for="(trItem, trIndex) in tableComputed" :key="trIndex"> |
|||
<tr class="q-tr--no-hover"> |
|||
<template v-for="(tdItem, tdIndex) in trItem as any" :key="tdIndex"> |
|||
<td :class="labelAlignClassComputed">{{ tdItem.label }}</td> |
|||
<td :class="valueAlignClassComputed"> |
|||
<template v-if="tdItem.value && typeof tdItem.value === 'object' && tdItem.value.type && tdItem.value._vuecomp_"> |
|||
<component :is="tdItem.value.type" v-bind="tdItem.value.props"></component> |
|||
</template> |
|||
<template v-else> |
|||
{{ tdItem.value }} |
|||
</template> |
|||
</td> |
|||
</template> |
|||
</tr> |
|||
</template> |
|||
</tbody> |
|||
</q-markup-table> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { computed, useAttrs } from 'vue'; |
|||
import { getCssVar } from 'quasar'; |
|||
import { Environment } from '@/platform'; |
|||
|
|||
const attrs = useAttrs(); |
|||
const gc = Environment.getConfigure(); |
|||
const darkBgColor = getCssVar('dark'); |
|||
const bgColor = gc.theme.dark ? darkBgColor : gc.theme?.grid?.headBgColor || '#f5f7fa'; |
|||
const props = defineProps({ |
|||
columnNum: { type: Number, default: 1 }, |
|||
labelAlign: { type: String, default: 'left' }, |
|||
valueAlign: { type: String, default: 'left' }, |
|||
info: { |
|||
type: Array, |
|||
default: () => { |
|||
return []; |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
const tableComputed = computed(() => { |
|||
const table = <any>[]; |
|||
let tmp = <any>[]; |
|||
props.info.forEach((item, index) => { |
|||
if (tmp.length < props.columnNum) { |
|||
tmp.push(item); |
|||
} else { |
|||
table.push(tmp); |
|||
tmp = []; |
|||
tmp.push(item); |
|||
} |
|||
}); |
|||
table.push(tmp); |
|||
return table; |
|||
}); |
|||
const labelAlignClassComputed = computed(() => { |
|||
let className = ''; |
|||
switch (props.labelAlign) { |
|||
case 'right': |
|||
className = 'text-right'; |
|||
break; |
|||
case 'center': |
|||
className = 'text-center'; |
|||
break; |
|||
default: |
|||
className = 'text-left'; |
|||
} |
|||
return className; |
|||
}); |
|||
const valueAlignClassComputed = computed(() => { |
|||
let className = ''; |
|||
switch (props.valueAlign) { |
|||
case 'right': |
|||
className = 'text-right'; |
|||
break; |
|||
case 'center': |
|||
className = 'text-center'; |
|||
break; |
|||
default: |
|||
className = 'text-left'; |
|||
} |
|||
return className; |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.q-table td:nth-child(odd) { |
|||
background-color: v-bind(bgColor); |
|||
} |
|||
</style> |
@ -0,0 +1,57 @@ |
|||
<template> |
|||
<q-item clickable :disable="button[0].enableIf ? !button[0].enableIf(gridProp.gridSelected) : false"> |
|||
<q-item-section> |
|||
<q-item-label><q-icon v-if="button[0].icon" :name="button[0].icon" size="20px"></q-icon> {{ button[0].label }}</q-item-label> |
|||
</q-item-section> |
|||
<q-item-section side> |
|||
<q-icon name="keyboard_arrow_right" /> |
|||
</q-item-section> |
|||
|
|||
<q-menu anchor="top end" self="top start"> |
|||
<q-list> |
|||
<template v-for="(childrenBtn, index) in button" :key="index"> |
|||
<template v-if="index === 0 && !childrenBtn.click"></template> |
|||
<q-separator v-else-if="typeof childrenBtn === 'string' && childrenBtn === 'separator'" /> |
|||
<ChildrenBtn v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0" :button="childrenBtn"></ChildrenBtn> |
|||
<template v-else> |
|||
<q-item |
|||
v-close-popup |
|||
clickable |
|||
:disable="childrenBtn.enableIf ? !childrenBtn.enableIf(gridProp.gridSelected) : false" |
|||
@click="buttonClick(childrenBtn)" |
|||
> |
|||
<q-item-section> |
|||
<q-item-label><q-icon v-if="childrenBtn.icon" :name="childrenBtn.icon" size="20px"></q-icon> {{ childrenBtn.label }}</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</template> |
|||
</q-list> |
|||
</q-menu> |
|||
</q-item> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
const props = defineProps({ |
|||
button: { |
|||
type: Object, |
|||
default: () => { |
|||
return []; |
|||
}, |
|||
}, |
|||
gridProp: { |
|||
type: Object, |
|||
default: () => { |
|||
return { |
|||
gridSelected: [], |
|||
gridRefs: {}, |
|||
}; |
|||
}, |
|||
}, |
|||
buttonClick: { |
|||
type: Function, |
|||
default: () => { |
|||
return () => {}; |
|||
}, |
|||
}, |
|||
}); |
|||
</script> |
@ -0,0 +1,284 @@ |
|||
<template> |
|||
<div> |
|||
<div |
|||
ref="containerRef" |
|||
:class="'flex ' + (align === 'left' ? 'justify-start' : align === 'center' ? 'justify-center' : 'justify-end') + ' gap-x-[' + props.xGap + 'px]'" |
|||
> |
|||
<!-- buttons --> |
|||
<template v-for="(btn, index) in baseActions" :key="'button_' + index"> |
|||
<q-separator v-if="typeof btn.data === 'string' && btn.data === 'separator'" vertical class="class-action-item" /> |
|||
<q-btn-dropdown |
|||
v-else-if="Array.isArray(btn.data) && btn.data.length > 0" |
|||
unelevated |
|||
outline |
|||
v-bind="btn.data[0]" |
|||
:split="btn.data[0].click ? true : false" |
|||
:disable="btn.data[0].enableIf ? !btn.data[0].enableIf(gridProp.gridSelected) : false" |
|||
class="class-action-item" |
|||
@click="buttonClick(btn.data[0])" |
|||
> |
|||
<q-list> |
|||
<template v-for="(childrenBtn, childrenIndex) in btn.data" :key="'button_c_' + childrenIndex"> |
|||
<template v-if="childrenIndex === 0"></template> |
|||
<template v-else> |
|||
<q-separator v-if="typeof childrenBtn === 'string' && childrenBtn === 'separator'" /> |
|||
<ChildrenBtn |
|||
v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0" |
|||
:button="childrenBtn" |
|||
:grid-prop="gridProp" |
|||
:button-click="buttonClick" |
|||
></ChildrenBtn> |
|||
<q-item |
|||
v-else |
|||
v-close-popup |
|||
clickable |
|||
:disable="childrenBtn.enableIf ? !childrenBtn.enableIf(gridProp.gridSelected) : false" |
|||
@click="buttonClick(childrenBtn)" |
|||
> |
|||
<q-item-section> |
|||
<q-item-label><q-icon v-if="childrenBtn.icon" :name="childrenBtn.icon" size="20px"></q-icon> {{ childrenBtn.label }}</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</template> |
|||
</q-list> |
|||
</q-btn-dropdown> |
|||
<q-btn |
|||
v-else |
|||
:disable="btn.data.enableIf ? !btn.data.enableIf(gridProp.gridSelected) : false" |
|||
no-wrap |
|||
outline |
|||
v-bind="btn.data" |
|||
class="class-action-item" |
|||
@click="buttonClick(btn.data)" |
|||
/> |
|||
</template> |
|||
|
|||
<!-- moreActions --> |
|||
<q-btn-dropdown v-if="moreActions && moreActions.length > 0" unelevated outline :label="$t('more')" class="class-action-item"> |
|||
<q-list> |
|||
<template v-for="(childrenBtn, childrenIndex) in moreActions" :key="'moreAction_' + childrenIndex"> |
|||
<q-separator v-if="typeof childrenBtn.data === 'string' && childrenBtn.data === 'separator'" /> |
|||
<ChildrenBtn |
|||
v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0" |
|||
:button="childrenBtn.data" |
|||
:grid-prop="gridProp" |
|||
:button-click="buttonClick" |
|||
></ChildrenBtn> |
|||
<q-item |
|||
v-else |
|||
v-close-popup |
|||
clickable |
|||
:disable="childrenBtn.data.enableIf ? !childrenBtn.data.enableIf(gridProp.gridSelected) : false" |
|||
@click="buttonClick(childrenBtn.data)" |
|||
> |
|||
<q-item-section> |
|||
<q-item-label |
|||
><q-icon v-if="childrenBtn.data.icon" :name="childrenBtn.data.icon" size="20px"></q-icon> {{ childrenBtn.data.label }}</q-item-label |
|||
> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</q-list> |
|||
</q-btn-dropdown> |
|||
</div> |
|||
<q-resize-observer @resize="onResize" /> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive, nextTick } from 'vue'; |
|||
import { Tools } from '@/platform/utils'; |
|||
import ChildrenBtn from './ChildrenBtn.vue'; |
|||
|
|||
const props = defineProps({ |
|||
xGap: { type: Number, default: 4 }, // 按扭之间x轴间隔像素点 |
|||
noIcon: { type: Boolean, default: false }, // 无icon模式 |
|||
align: { type: String, default: 'right' }, // 对齐方式 |
|||
buttons: { |
|||
type: Array, |
|||
default: () => { |
|||
return []; |
|||
}, |
|||
}, |
|||
gridProp: { |
|||
type: Object, |
|||
default: () => { |
|||
return { |
|||
gridSelected: [], |
|||
gridRefs: {}, |
|||
}; |
|||
}, |
|||
}, |
|||
}); |
|||
const containerRef = ref(); |
|||
|
|||
// 将所有按钮根据 name 提取到 JSON 对象中 |
|||
const buttonsJson = {}; |
|||
const extractButton = (btns: any) => { |
|||
for (const btn of btns) { |
|||
if (Array.isArray(btn)) { |
|||
extractButton(btn); |
|||
} else if (typeof btn === 'object') { |
|||
buttonsJson[btn.name] = { ...btn }; |
|||
if (props.noIcon) { |
|||
buttonsJson[btn.name].icon = undefined; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
extractButton(props.buttons); |
|||
|
|||
// // 将按钮形成父子关系 |
|||
// const handleChildren = (parent, children, arr) => { |
|||
// for (const item of arr) { |
|||
// if (Array.isArray(item)) { |
|||
// const other = item.splice(1); |
|||
// const children_ = <any>[]; |
|||
// handleChildren(item[0].name, children_, other); |
|||
// children.push({ parent, children: children_, button: buttonsJson[item[0].name] }); |
|||
// } else if (typeof item === 'object') { |
|||
// children.push({ parent, children: null, button: buttonsJson[item.name] }); |
|||
// } else if (typeof item === 'string' && item === 'separator') { |
|||
// // 当最后一个元素是分割符时,不再追加分割符,避免两个按钮之间出现重复的无意义分割符 |
|||
// if (children.length > 0 && children[children.length - 1].button !== 'separator') { |
|||
// children.push({ parent, children: null, button: 'separator' }); |
|||
// } |
|||
// } |
|||
// } |
|||
// }; |
|||
// const buttons_ = <any>[]; |
|||
// for (const btn of props.buttons) { |
|||
// if (Array.isArray(btn)) { |
|||
// const other = btn.splice(1); |
|||
// const children = <any>[]; |
|||
// handleChildren(btn[0].name, children, other); |
|||
// buttons_.push({ parent: null, children: children, button: buttonsJson[btn[0].name] }); |
|||
// } else if (typeof btn === 'object') { |
|||
// buttons_.push({ parent: null, children: null, button: buttonsJson[btn.name] }); |
|||
// } else if (typeof btn === 'string' && btn === 'separator') { |
|||
// // 当最后一个元素是分割符时,不再追加分割符,避免两个按钮之间出现重复的无意义分割符 |
|||
// if (buttons_.length > 0 && buttons_[buttons_.length - 1].button !== 'separator') { |
|||
// buttons_.push({ parent: null, children: null, button: 'separator' }); |
|||
// } |
|||
// } |
|||
// } |
|||
|
|||
const buttons_ = <any>[]; |
|||
const handleChildrenSeparator = (arr) => { |
|||
const tempArr = []; |
|||
for (let i = 0; i < arr.length; i++) { |
|||
const btn = arr[i]; |
|||
if (i === 0 && typeof btn === 'string' && btn === 'separator') { |
|||
// 按钮数组第一个元素为分割符时直接跳过 |
|||
continue; |
|||
} |
|||
if (typeof btn === 'string' && btn === 'separator' && tempArr.length > 0 && tempArr[tempArr.length - 1] === btn) { |
|||
// 连续的分割符直接跳过,避免出现重复的无意义分割符 |
|||
continue; |
|||
} |
|||
if (Array.isArray(btn) && btn.length > 0) { |
|||
const handleResult = handleChildrenSeparator(btn); |
|||
if (handleResult && handleResult.length > 0) { |
|||
tempArr.push(handleResult); |
|||
} |
|||
} else if (typeof btn === 'string' && btn === 'separator') { |
|||
tempArr.push(btn); |
|||
} else { |
|||
tempArr.push(buttonsJson[btn.name]); |
|||
} |
|||
} |
|||
return tempArr; |
|||
}; |
|||
for (let i = 0; i < props.buttons.length; i++) { |
|||
const btn = props.buttons[i]; |
|||
if (i === 0 && typeof btn === 'string' && btn === 'separator') { |
|||
// 按钮数组第一个元素为分割符时直接跳过 |
|||
continue; |
|||
} |
|||
if (typeof btn === 'string' && btn === 'separator' && buttons_.length > 0 && buttons_[buttons_.length - 1].data === btn) { |
|||
// 连续的分割符直接跳过,避免出现重复的无意义分割符 |
|||
continue; |
|||
} |
|||
if (Array.isArray(btn) && btn.length > 0) { |
|||
buttons_.push({ data: handleChildrenSeparator(btn) }); |
|||
} else if (typeof btn === 'string' && btn === 'separator') { |
|||
buttons_.push({ data: btn }); |
|||
} else { |
|||
buttons_.push({ data: buttonsJson[btn.name] }); |
|||
} |
|||
} |
|||
const baseActions = ref(buttons_); |
|||
const moreActions = ref([]); |
|||
const isActionWidthInitializedRef = ref(false); |
|||
const moreActionWidth = 100; |
|||
const onResize = (size) => { |
|||
if (Tools.isUndefinedOrNull(containerRef.value)) { |
|||
return; |
|||
} |
|||
|
|||
if (!isActionWidthInitializedRef.value) { |
|||
const nodes = containerRef.value.getElementsByClassName('class-action-item'); |
|||
for (let i = 0; i < buttons_.length; i++) { |
|||
buttons_[i].width = nodes[i].clientWidth; |
|||
} |
|||
isActionWidthInitializedRef.value = true; |
|||
} |
|||
|
|||
const _baseActions = []; |
|||
const _moreActions = []; |
|||
const length = buttons_.length; |
|||
let availableWidth = size.width; |
|||
let width = 0; |
|||
let index = 0; |
|||
|
|||
for (; index < length; index++) { |
|||
if (width + buttons_[index].width > availableWidth) { |
|||
availableWidth -= moreActionWidth; |
|||
while (width > availableWidth) { |
|||
index--; |
|||
width -= buttons_[index].width; |
|||
_baseActions.pop(); |
|||
} |
|||
break; |
|||
} else { |
|||
_baseActions.push(buttons_[index]); |
|||
width += buttons_[index].width; |
|||
width += props.xGap; |
|||
} |
|||
} |
|||
|
|||
for (; index < length; index++) { |
|||
_moreActions.push(buttons_[index]); |
|||
} |
|||
|
|||
baseActions.value = _baseActions; |
|||
moreActions.value = _moreActions; |
|||
}; |
|||
|
|||
const buttonClick = async (button) => { |
|||
let beforeResult = true; |
|||
const context = {}; |
|||
if (button.beforeClick) { |
|||
beforeResult = await button.beforeClick(props.gridProp.gridSelected, context, props.gridProp.gridRefs); |
|||
} |
|||
if (beforeResult && button.click) { |
|||
await button.click(props.gridProp.gridSelected, context, button._click, props.gridProp.gridRefs); |
|||
if (button.afterClick) { |
|||
nextTick(() => { |
|||
button.afterClick(props.gridProp.gridSelected, context, props.gridProp.gridRefs); |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped lang="css"> |
|||
.q-btn.disabled { |
|||
opacity: 0.7 !important; |
|||
color: #a8a8a8; |
|||
} |
|||
.q-item.disabled { |
|||
opacity: 0.6 !important; |
|||
color: #a8a8a8; |
|||
} |
|||
</style> |
@ -0,0 +1,58 @@ |
|||
/** |
|||
* Form表单元素验证器 |
|||
*/ |
|||
export class FormValidators { |
|||
/** |
|||
* 必填项验证 |
|||
* @returns |
|||
*/ |
|||
public static required(msg: string) { |
|||
return (val) => { |
|||
if (val === null || val === undefined) { |
|||
return msg || '必填项未填写'; |
|||
} |
|||
if (typeof val === 'string') { |
|||
return val !== '' || msg || '必填项未填写'; |
|||
} else if (Array.isArray(val)) { |
|||
return val.length > 0 || msg || '必填项未填写'; |
|||
} else { |
|||
return true; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 长度范围验证 |
|||
* @param min 最小长度 |
|||
* @param max 最大长度 |
|||
* @returns |
|||
*/ |
|||
public static lengthRange(min: number, max: number, msg: string) { |
|||
return (val) => { |
|||
const tmp = String(val); |
|||
if (tmp === null || tmp === '' || (tmp.length >= min && tmp.length <= max)) { |
|||
return true; |
|||
} else { |
|||
return msg || '长度不符合要求(' + min + '-' + max + ')'; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 数字最大精度校验 |
|||
* @param precision 最大位数 |
|||
* @returns |
|||
*/ |
|||
public static maxPrecision(precision: number, msg: string) { |
|||
return (val) => { |
|||
const tmp = String(val); |
|||
if (val === null || tmp === '' || tmp.indexOf('.') === -1 || tmp.substring(tmp.indexOf('.') + 1).length <= precision) { |
|||
return true; |
|||
} else if (precision === 0) { |
|||
return '只能输入整数'; |
|||
} else { |
|||
return msg || '最大允许输入的小数位:' + precision; |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
<template> |
|||
<div> |
|||
<w-form :fields="fields" :cols-num-auto="false" :cols-num="3"></w-form> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
const fields = [ |
|||
{ |
|||
label: '姓名', |
|||
name: 'name', |
|||
type: 'text', |
|||
required: true, |
|||
colspan: 2, |
|||
button: { |
|||
round: false, |
|||
icon: 'home', |
|||
label: '选择', |
|||
click: () => { |
|||
console.info('11111'); |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
label: '年龄', |
|||
name: 'age', |
|||
type: 'number', |
|||
required: true, |
|||
}, |
|||
]; |
|||
</script> |
@ -0,0 +1,4 @@ |
|||
<template> |
|||
<div>2222</div> |
|||
</template> |
|||
<script setup lang="ts"></script> |
@ -0,0 +1,13 @@ |
|||
<template> |
|||
<q-input ref="aaaref" :model-value="a" label="dddd"></q-input> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue'; |
|||
const aaaref = ref(); |
|||
const a = ref(''); |
|||
|
|||
onMounted(() => { |
|||
aaaref.value.focus(); |
|||
console.info('aaaref====', aaaref); |
|||
}); |
|||
</script> |
@ -0,0 +1,35 @@ |
|||
<template> |
|||
<div> |
|||
<w-dialog |
|||
ref="dialogRef" |
|||
:buttons="[ |
|||
{ |
|||
label: '测试', |
|||
click: aaaaa, |
|||
}, |
|||
]" |
|||
@hide="aaaaa" |
|||
> |
|||
<q-splitter v-model="aa" style="height: 100%"> |
|||
<template #before> 11111 </template> |
|||
<template #after> 22222 </template> |
|||
</q-splitter> |
|||
<template #buttons> <q-btn label="xxx"></q-btn> </template |
|||
></w-dialog> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive, onMounted } from 'vue'; |
|||
|
|||
const dialogRef = ref(); |
|||
|
|||
const aa = ref(50); |
|||
const aaaaa = (e) => { |
|||
console.info('dddddddddddddd', e); |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
console.info('dialogRef====', dialogRef); |
|||
dialogRef.value.show(); |
|||
}); |
|||
</script> |
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<div> |
|||
<q-btn label="弹出" @click="click"></q-btn> |
|||
<w-drawer |
|||
ref="drawerRef" |
|||
title="xxx" |
|||
:maximized="false" |
|||
:buttons="[ |
|||
{ |
|||
label: '测试', |
|||
}, |
|||
]" |
|||
> |
|||
1111 |
|||
<template #buttons> <q-btn label="xxx"></q-btn> </template> |
|||
</w-drawer> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive, onMounted } from 'vue'; |
|||
import { IconEnum } from '@/platform/enums'; |
|||
|
|||
const drawerRef = ref(); |
|||
|
|||
const aa = ref(50); |
|||
const aaaaa = (e) => { |
|||
console.info('dddddddddddddd', e); |
|||
}; |
|||
const click = () => { |
|||
drawerRef.value.show(); |
|||
}; |
|||
|
|||
onMounted(() => {}); |
|||
</script> |
@ -0,0 +1,152 @@ |
|||
<template> |
|||
<div> |
|||
<w-form ref="formRef" :fields="aaaa.fields" :cols-x-gap="8"> </w-form> |
|||
<q-btn label="提交" @click="submit"></q-btn> <q-btn label="重置" @click="reset"></q-btn> |
|||
<q-btn label="初始化值" @click="setValue"></q-btn> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive } from 'vue'; |
|||
import { FormValidators } from '@/platform/components'; |
|||
const formRef = ref(); |
|||
const aaaa = { |
|||
fields: [ |
|||
{ |
|||
label: '姓名', |
|||
name: 'name', |
|||
type: 'w-text', |
|||
// maxlength: 10, |
|||
rules: [FormValidators.lengthRange(5, 10, '22222')], |
|||
defaultValue: '123', |
|||
colspan: 2, |
|||
requiredIf: () => true, |
|||
'onUpdate:modelValue': () => {}, |
|||
onFocus: () => { |
|||
// console.info('33333333'); |
|||
}, |
|||
}, |
|||
{ |
|||
label: '机构', |
|||
name: 'org', |
|||
type: 'w-text-btn', |
|||
requiredIf: () => true, |
|||
// buttonPosition: 'prepend', |
|||
button: { |
|||
icon: 'home', |
|||
click: () => { |
|||
console.info('点击!!'); |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
label: '年龄', |
|||
name: 'age', |
|||
type: 'w-number', |
|||
defaultValue: 111, |
|||
precision: 0, |
|||
requiredIf: () => true, |
|||
}, |
|||
{ |
|||
label: '出生日期', |
|||
name: 'cs', |
|||
type: 'w-date', |
|||
requiredIf: () => true, |
|||
}, |
|||
{ |
|||
label: '爱好', |
|||
name: 'ah', |
|||
type: 'w-text', |
|||
requiredIf: () => true, |
|||
}, |
|||
{ |
|||
label: '性别', |
|||
name: 'sex', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ |
|||
label: '男', |
|||
value: 1, |
|||
}, |
|||
{ |
|||
label: '女', |
|||
value: 0, |
|||
}, |
|||
], |
|||
requiredIf: () => true, |
|||
'onUpdate:modelValue': (value) => { |
|||
if (value === 1) { |
|||
formRef.value.fieldsMap.get('addr').label = '地址2222'; |
|||
} else { |
|||
formRef.value.fieldsMap.get('addr').label = '地址3333'; |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
label: '地址', |
|||
name: 'addr', |
|||
rows: 2, |
|||
colspan: 'full', |
|||
type: 'w-textarea', |
|||
hideIf: () => { |
|||
if (formRef.value) { |
|||
if (formRef.value.data.sex && formRef.value.data.sex === 1) { |
|||
return false; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
return false; |
|||
}, |
|||
requiredIf: () => true, |
|||
// readonlyIf: () => { |
|||
// return true; |
|||
// }, |
|||
disableIf: () => { |
|||
return false; |
|||
}, |
|||
}, |
|||
{ |
|||
label: '是否可用1', |
|||
name: 'ky1', |
|||
colsFirst: true, |
|||
colspan: 2, |
|||
type: 'w-checkbox', |
|||
// disableIf: () => { |
|||
// return true; |
|||
// }, |
|||
}, |
|||
{ |
|||
label: '是否可用2', |
|||
name: 'ky2', |
|||
type: 'w-checkbox', |
|||
}, |
|||
{ |
|||
label: '是否可用3', |
|||
name: 'ky3', |
|||
type: 'w-checkbox', |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const submit = async () => { |
|||
aaaa.fields[1].required = false; |
|||
const validateResult = await formRef.value.validate(); |
|||
console.info('表单验证结果:', validateResult); |
|||
console.info('数据:', formRef.value.getData()); |
|||
}; |
|||
const reset = () => { |
|||
formRef.value.reset(); |
|||
}; |
|||
const setValue = () => { |
|||
const data = { |
|||
name: '张三', |
|||
age: 18, |
|||
cs: '2023-12-25', |
|||
ah: '唱、跳、篮球', |
|||
ky: true, |
|||
sex: 1, |
|||
addr: '上海市', |
|||
}; |
|||
formRef.value.setData(data); |
|||
}; |
|||
</script> |
@ -0,0 +1,220 @@ |
|||
<template> |
|||
<div> |
|||
<w-grid |
|||
:title="testGrid.title" |
|||
:row-drag="true" |
|||
:row-key="testGrid.rowKey" |
|||
:auto-load-data="testGrid.autoLoadData" |
|||
:get-data-url="testGrid.tableDataUrl" |
|||
:show-sort-no="testGrid.tableShowSortNo" |
|||
:columns="testGrid.tableColumns" |
|||
:left-column-sticky-number="testGrid.tableLeftColumnStickyNumber" |
|||
:column-title-dense="true" |
|||
:toolbar="testGrid.toolbar" |
|||
:query-form="testGrid.queryForm" |
|||
:table-pagination="testGrid.tablePagination" |
|||
:add-edit="testGrid.addEdit" |
|||
:view="testGrid.view" |
|||
@selection="selection" |
|||
@row-click="rowClick" |
|||
@row-db-click="rowDbClick" |
|||
></w-grid> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, onMounted, nextTick } from 'vue'; |
|||
import { axios, Environment } from '@/platform'; |
|||
import EnableIcon from '@/platform/components/grid/EnableIcon.vue'; |
|||
import { IconEnum } from '@/platform/enums'; |
|||
|
|||
const selection = (details) => { |
|||
console.info('details====', details); |
|||
}; |
|||
const rowClick = (evt, row, index) => { |
|||
console.info('row====', row); |
|||
}; |
|||
const rowDbClick = (evt, row, index) => { |
|||
console.info('row1====', row); |
|||
}; |
|||
const testGrid = { |
|||
hideBottom: false, |
|||
autoLoadData: true, |
|||
tableLeftColumnStickyNumber: 0, |
|||
title: '用户列表', |
|||
rowKey: 'id', |
|||
tableDataUrl: Environment.apiContextPath('api/system/user'), |
|||
tablePagination: { |
|||
sortBy: 'lastModifyDate', |
|||
descending: true, |
|||
reqPageStart: 0, |
|||
rowsPerPage: 10, |
|||
}, |
|||
toolbar: { |
|||
buttons: [ |
|||
['query', 'separator', 'moreQuery'], |
|||
'reset', |
|||
'refresh', |
|||
'separator', |
|||
{ |
|||
name: 'custBtn', |
|||
extend: 'add', |
|||
icon: undefined, |
|||
label: '自定义按钮', |
|||
enableIf: (selected) => { |
|||
if (selected && selected.length > 0) { |
|||
return true; |
|||
} |
|||
return false; |
|||
}, |
|||
// beforeClick: (selected, context, gridRefs) => { |
|||
// console.info('先执行before'); |
|||
// context.aaa = '111'; |
|||
// }, |
|||
click: (selected, context, _click, gridRefs) => { |
|||
_click(); |
|||
}, |
|||
afterClick: (selected, context, gridRefs) => { |
|||
gridRefs.addEditFormRef.setFieldValue('userName', '李四'); |
|||
}, |
|||
}, |
|||
[ |
|||
{ |
|||
name: 'op', |
|||
icon: 'difference', |
|||
label: '操作', |
|||
}, |
|||
'add', |
|||
'edit', |
|||
'clone', |
|||
'remove', |
|||
'separator', |
|||
'view', |
|||
'export', |
|||
], |
|||
'separator', |
|||
], |
|||
}, |
|||
tableShowSortNo: true, |
|||
queryForm: { |
|||
rowNum: 1, |
|||
fields: [ |
|||
{ label: '登录名', name: 'loginName', type: 'w-text' }, |
|||
{ label: '用户名', name: 'userName', type: 'w-text' }, |
|||
{ label: '描述', name: 'description', type: 'w-text' }, |
|||
{ label: '是否可用', name: 'enable', type: 'w-select' }, |
|||
// { label: '邮箱地址', name: 'email', type: 'w-text' }, |
|||
// { label: '电话', name: 'phone', type: 'w-text' }, |
|||
// { label: '手机号', name: 'mobile', type: 'w-number' }, |
|||
// { label: '最后修改人', name: 'lastModifier', type: 'w-text' }, |
|||
// { label: '最后修改时间', name: 'lastModifyDate', type: 'w-date' }, |
|||
], |
|||
}, |
|||
tableColumns: [ |
|||
{ |
|||
name: 'info', |
|||
label: '用户信息', |
|||
childrenColumns: [ |
|||
{ name: 'loginName', label: '登录名', align: 'right' }, |
|||
{ name: 'userName', label: '用户名' }, |
|||
], |
|||
}, |
|||
{ |
|||
name: 'lxxx', |
|||
label: '联系方式', |
|||
childrenColumns: [ |
|||
{ name: 'email', label: '邮箱地址' }, |
|||
{ |
|||
name: 'tx', |
|||
label: '通讯', |
|||
childrenColumns: [ |
|||
{ name: 'phone', label: '电话' }, |
|||
{ name: 'mobile', label: '手机号' }, |
|||
], |
|||
}, |
|||
{ name: 'qq', label: 'QQ' }, |
|||
], |
|||
}, |
|||
{ name: 'description', label: '描述' }, |
|||
{ |
|||
name: 'enable', |
|||
label: '是否可用', |
|||
align: 'center', |
|||
format: (val, row) => { |
|||
// return { |
|||
// _vuecomp_: true, |
|||
// type: EnableIcon, |
|||
// props: { |
|||
// value: val, |
|||
// showNoEnable: true, |
|||
// }, |
|||
// }; |
|||
// return { |
|||
// _vuecomp_: true, |
|||
// type: 'q-chip', |
|||
// props: { |
|||
// label: val ? '是' : '否', |
|||
// icon: val ? IconEnum.是状态 : IconEnum.否状态, |
|||
// color: val ? 'green' : 'red', |
|||
// }, |
|||
// }; |
|||
return { |
|||
_vuecomp_: true, |
|||
type: 'q-icon', |
|||
props: { |
|||
name: val ? IconEnum.是状态 : IconEnum.否状态, |
|||
color: val ? 'green' : 'red', |
|||
size: 'sm', |
|||
}, |
|||
}; |
|||
// return { |
|||
// _vuecomp_: true, |
|||
// type: 'w-text', |
|||
// props: { |
|||
// name: 'aaa', |
|||
// label: '请输入', |
|||
// }, |
|||
// }; |
|||
}, |
|||
}, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
addEdit: { |
|||
dialog: {}, |
|||
form: { |
|||
colsNum: 1, |
|||
fields: [ |
|||
{ label: '登录名', name: 'loginName', type: 'w-text' }, |
|||
{ label: '用户名', name: 'userName', type: 'w-text' }, |
|||
{ label: '密码', name: 'password', type: 'w-text' }, |
|||
{ label: '是否可用1111', name: 'enable', type: 'w-checkbox' }, |
|||
], |
|||
}, |
|||
}, |
|||
view: { |
|||
infoPanel: { |
|||
columnNum: 2, |
|||
fields: [ |
|||
{ name: 'id', label: '主键' }, |
|||
{ name: 'loginName', label: '登录名' }, |
|||
{ name: 'userName', label: '用户名' }, |
|||
{ name: 'description', label: '描述' }, |
|||
{ |
|||
name: 'enable', |
|||
label: '是否可用', |
|||
}, |
|||
{ name: 'email', label: '邮箱地址' }, |
|||
{ name: 'phone', label: '电话' }, |
|||
{ name: 'mobile', label: '手机号' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
}, |
|||
}, |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
// testGridRef.value.replaceRowsFun([{ typeName: '股权投资信息', typeDesc: '股权投资信息', lastModifier: 'admin', lastModifyDate: '2023-12-26' }]); |
|||
}); |
|||
</script> |
@ -0,0 +1,308 @@ |
|||
<template> |
|||
<q-splitter :model-value="70" class="w-full h-full"> |
|||
<template #before> |
|||
<w-grid |
|||
ref="userGridRef" |
|||
:title="userConfigure.tableTitle" |
|||
:row-key="userConfigure.tableRowKey" |
|||
:auto-load-data="userConfigure.tableInitLoadData" |
|||
:get-data-url="userConfigure.tableDataUrl" |
|||
:show-sort-no="false" |
|||
:columns="userConfigure.tableColumns" |
|||
:left-column-sticky-number="userConfigure.tableLeftColumnStickyNumber" |
|||
:toolbar="userConfigure.toolbar" |
|||
:query-form="userConfigure.queryForm" |
|||
:table-pagination="userConfigure.tablePagination" |
|||
@row-click="userConfigure.rowClickFun" |
|||
></w-grid> |
|||
</template> |
|||
<template #after> |
|||
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0"> |
|||
<q-tab name="role" icon="bi-people" :label="$t('role')" /> |
|||
<q-tab name="org" icon="bi-diagram-3" :label="$t('org')" /> |
|||
</q-tabs> |
|||
|
|||
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive> |
|||
<q-tab-panel name="role" class="p-0"> |
|||
<w-grid |
|||
ref="roleGridRef" |
|||
selection="multiple" |
|||
:hide-bottom="false" |
|||
:title="roleConfigure.tableTitle" |
|||
:row-key="roleConfigure.tableRowKey" |
|||
:auto-load-data="roleConfigure.tableInitLoadData" |
|||
:get-data-url="roleConfigure.tableDataUrl" |
|||
:show-sort-no="false" |
|||
:columns="roleConfigure.tableColumns" |
|||
:left-column-sticky-number="roleConfigure.tableLeftColumnStickyNumber" |
|||
:toolbar="roleConfigure.toolbar" |
|||
:table-pagination="roleConfigure.tablePagination" |
|||
></w-grid> |
|||
</q-tab-panel> |
|||
<q-tab-panel name="org"> 11111 </q-tab-panel> |
|||
</q-tab-panels> |
|||
</template> |
|||
</q-splitter> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { Environment, axios } from '@/platform'; |
|||
|
|||
const { t } = useI18n(); |
|||
|
|||
const userGridRef = ref(); |
|||
const roleGridRef = ref(); |
|||
const orgTreeGridRef = ref(); |
|||
|
|||
const selectRoleDialog = ref(); |
|||
const changePasswordDialog = ref(); |
|||
|
|||
const selectedTabRef = ref('role'); |
|||
|
|||
const userConfigure = { |
|||
queryForm: { |
|||
fields: [ |
|||
{ label: t('loginName'), name: 'loginName', type: 'w-text' }, |
|||
{ label: t('userName'), name: 'userName', type: 'w-text' }, |
|||
{ |
|||
label: t('enable'), |
|||
name: 'enable', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ value: true, label: '是' }, |
|||
{ value: false, label: '否' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: t('dataComeFrom'), |
|||
name: 'dataComeFrom', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ value: 'MANUAL', label: t('io.sc.platform.orm.api.enums.DataComeFrom.MANUAL') }, |
|||
{ value: 'AUTO', label: t('io.sc.platform.orm.api.enums.DataComeFrom.AUTO') }, |
|||
], |
|||
}, |
|||
], |
|||
}, |
|||
hideBottom: false, |
|||
tableInitLoadData: true, |
|||
tableLeftColumnStickyNumber: 0, |
|||
tableTitle: t('system.user.gridTitle'), |
|||
tableRowKey: 'id', |
|||
tableDataUrl: Environment.apiContextPath('/api/system/user'), |
|||
tablePagination: { |
|||
sortBy: 'lastModifyDate', |
|||
descending: true, |
|||
reqPageStart: 0, |
|||
rowsPerPage: 10, |
|||
}, |
|||
toolbar: { |
|||
buttons: [ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
'refresh', |
|||
'add', |
|||
'edit', |
|||
'remove', |
|||
'separator', |
|||
'view', |
|||
'separator', |
|||
{ |
|||
name: 'setPassword', |
|||
label: t('system.user.action.setPassword'), |
|||
icon: 'bi-shield-exclamation', |
|||
enableIf: function () {}, |
|||
click: function () { |
|||
changePasswordDialog.value.show(); |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
tableColumns: [ |
|||
{ width: 100, name: 'loginName', label: t('loginName') }, |
|||
{ width: 100, name: 'userName', label: t('userName') }, |
|||
{ width: 100, name: 'enable', label: t('isEnable'), format: (value) => (value ? t('yes') : t('no')) }, |
|||
{ width: 100, name: 'dataComeFrom', label: t('dataComeFrom') }, |
|||
{ width: 100, name: 'accountExpired', label: t('accountExpired'), format: (value) => (value ? t('yes') : t('no')) }, |
|||
{ width: 100, name: 'accountLocked', label: t('accountLocked'), format: (value) => (value ? t('yes') : t('no')) }, |
|||
{ width: 120, name: 'credentialsExpired', label: t('credentialsExpired'), format: (value) => (value ? t('yes') : t('no')) }, |
|||
{ width: 110, name: 'lastModifier', label: t('lastModifier') }, |
|||
{ width: 115, name: 'lastModifyDate', label: t('lastModifyDate') }, |
|||
], |
|||
addFormProps: { |
|||
dialogInitWidth: '50%', |
|||
dialogInitHeight: '90%', |
|||
formColsNumber: 1, |
|||
formColsAuto: false, |
|||
formFields: [ |
|||
{ modelName: 'loginName', label: t('loginName'), type: 'text', required: true }, |
|||
{ modelName: 'userName', label: t('userName'), type: 'text', required: true }, |
|||
{ modelName: 'password', label: t('password'), type: 'password' }, |
|||
{ modelName: 'confirmPassword', label: t('confirmPassword'), type: 'password' }, |
|||
{ modelName: 'description', label: t('description'), type: 'textarea' }, |
|||
{ modelName: 'email', label: t('email'), type: 'text' }, |
|||
{ modelName: 'phone', label: t('phone'), type: 'text' }, |
|||
{ modelName: 'mobile', label: t('mobile'), type: 'text' }, |
|||
{ modelName: 'weixin', label: t('weixin'), type: 'text' }, |
|||
{ modelName: 'qq', label: t('qq'), type: 'text' }, |
|||
{ modelName: 'enable', label: t('enable'), type: 'checkbox', defaultValue: true }, |
|||
{ modelName: 'accountExpired', label: t('accountExpired'), type: 'checkbox' }, |
|||
{ modelName: 'accountLocked', label: t('accountLocked'), type: 'checkbox' }, |
|||
{ modelName: 'credentialsExpired', label: t('credentialsExpired'), type: 'checkbox' }, |
|||
], |
|||
}, |
|||
rowClickFun: (evt, row, index) => { |
|||
if (roleGridRef.value) { |
|||
axios.get(Environment.apiContextPath('/api/system/role/queryRolesByUser?userId=') + row.id).then((response) => { |
|||
roleGridRef.value.replaceRowsFun(response.data.content); |
|||
}); |
|||
} |
|||
if (orgTreeGridRef.value) { |
|||
axios.get(Environment.apiContextPath('/api/system/org/listAllOrgsWithSelectedStatusByUser?userId=') + row.id).then((response) => { |
|||
orgTreeGridRef.value.setNodes(response.data); |
|||
}); |
|||
} |
|||
}, |
|||
}; |
|||
|
|||
const roleConfigure = { |
|||
queryFormColsNumber: 4, |
|||
queryFormColsAuto: false, |
|||
queryFormFields: [], |
|||
hideBottom: true, |
|||
tableInitLoadData: false, |
|||
tableLeftColumnStickyNumber: 0, |
|||
tableTitle: t('system.role.gridTitle'), |
|||
tableRowKey: 'id', |
|||
tableDataUrl: '', |
|||
tablePagination: { |
|||
sortBy: 'lastModifyDate', |
|||
descending: true, |
|||
reqPageStart: 0, |
|||
rowsPerPage: 0, |
|||
}, |
|||
toolbar: { |
|||
buttons: [ |
|||
'refresh', |
|||
{ |
|||
name: 'addRole', |
|||
label: t('system.role.action.addRole'), |
|||
icon: '', |
|||
enable: () => { |
|||
if (userGridRef.value) { |
|||
return userGridRef.value.getSelectedRows().length > 0; |
|||
} |
|||
return false; |
|||
}, |
|||
click: () => { |
|||
selectRoleDialog.value.show({ userGrid: userGridRef, roleGrid: roleGridRef }); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'addAllRole', |
|||
label: t('system.role.action.addAllRole'), |
|||
icon: '', |
|||
enable: () => { |
|||
if (userGridRef.value) { |
|||
console.log(userGridRef.value.getSelectedRows()); |
|||
return userGridRef.value.getSelectedRows().length > 0; |
|||
} |
|||
return false; |
|||
}, |
|||
click: () => { |
|||
axios |
|||
.post(Environment.apiContextPath('/api/system/user/addAllRoles'), { |
|||
one: userGridRef.value.getSelectedRows()[0].id, |
|||
many: [], |
|||
}) |
|||
.then((response) => { |
|||
axios |
|||
.get(Environment.apiContextPath('/api/system/role/queryRolesByUser?userId=') + userGridRef.value.getSelectedRows()[0].id) |
|||
.then((response) => { |
|||
roleGridRef.value.replaceRowsFun(response.data.content); |
|||
}); |
|||
}); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'removeRole', |
|||
label: t('system.role.action.removeRole'), |
|||
icon: '', |
|||
enable: () => { |
|||
if (userGridRef.value) { |
|||
return userGridRef.value.getSelectedRows().length > 0; |
|||
} |
|||
return false; |
|||
}, |
|||
click: () => { |
|||
const roleIds = []; |
|||
for (const role of roleGridRef.value.getSelectedRows()) { |
|||
roleIds.push(role.id); |
|||
} |
|||
axios |
|||
.post(Environment.apiContextPath('/api/system/user/removeRoles'), { |
|||
one: userGridRef.value.getSelectedRows()[0].id, |
|||
many: roleIds, |
|||
}) |
|||
.then((response) => { |
|||
axios |
|||
.get(Environment.apiContextPath('/api/system/role/queryRolesByUser?userId=') + userGridRef.value.getSelectedRows()[0].id) |
|||
.then((response) => { |
|||
roleGridRef.value.replaceRowsFun(response.data.content); |
|||
}); |
|||
}); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'removeAllRole', |
|||
label: t('system.role.action.removeAllRole'), |
|||
icon: '', |
|||
enable: () => { |
|||
if (userGridRef.value) { |
|||
return userGridRef.value.getSelectedRows().length > 0; |
|||
} |
|||
return false; |
|||
}, |
|||
click: () => { |
|||
axios |
|||
.post(Environment.apiContextPath('/api/system/user/removeAllRoles'), { |
|||
one: userGridRef.value.getSelectedRows()[0].id, |
|||
many: [], |
|||
}) |
|||
.then((response) => { |
|||
axios |
|||
.get(Environment.apiContextPath('/api/system/role/queryRolesByUser?userId=') + userGridRef.value.getSelectedRows()[0].id) |
|||
.then((response) => { |
|||
roleGridRef.value.replaceRowsFun(response.data.content); |
|||
}); |
|||
}); |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
tableColumns: [ |
|||
{ name: 'code', label: t('code') }, |
|||
{ name: 'name', label: t('name') }, |
|||
{ name: 'enable', label: t('isEnable'), format: (value) => (value ? t('yes') : t('no')) }, |
|||
], |
|||
}; |
|||
|
|||
const orgConfigure = { |
|||
actions: [ |
|||
{ |
|||
name: 'save', |
|||
label: '保存', |
|||
click: () => { |
|||
axios |
|||
.post(Environment.apiContextPath('/api/system/user/updateOrgs'), { |
|||
one: userGridRef.value.getSelectedRows()[0].id, |
|||
many: orgTreeGridRef.value.getTicked(), |
|||
}) |
|||
.then((response) => {}); |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
</script> |
@ -0,0 +1,15 @@ |
|||
<template> |
|||
<div> |
|||
<w-info-panel :column-num="2" label-align="center" value-align="center" :info="infoArray" dense> </w-info-panel> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive } from 'vue'; |
|||
|
|||
const infoArray = [ |
|||
{ label: '姓名', value: '张三' }, |
|||
{ label: '年龄', value: 18 }, |
|||
{ label: '性别', value: '男' }, |
|||
{ label: '爱好', value: '唱跳' }, |
|||
]; |
|||
</script> |
@ -0,0 +1,154 @@ |
|||
<template> |
|||
<div> |
|||
<q-table |
|||
title="Treats" |
|||
table-style="height: 500px;" |
|||
:rows="rows" |
|||
:columns="columns" |
|||
row-key="name" |
|||
:flat="true" |
|||
:selection="'single'" |
|||
:separator="'cell'" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
|
|||
const columns = [ |
|||
{ |
|||
name: 'name', |
|||
required: true, |
|||
label: 'Dessert (100g serving)', |
|||
align: 'left', |
|||
field: (row) => row.name, |
|||
format: (val) => `${val}`, |
|||
sortable: true, |
|||
}, |
|||
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true }, |
|||
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true }, |
|||
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' }, |
|||
{ name: 'protein', label: 'Protein (g)', field: 'protein' }, |
|||
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' }, |
|||
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|||
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|||
]; |
|||
|
|||
const rows = [ |
|||
{ |
|||
name: 'Frozen Yogurt', |
|||
calories: 159, |
|||
fat: 6.0, |
|||
carbs: 24, |
|||
protein: 4.0, |
|||
sodium: 87, |
|||
calcium: '14%', |
|||
iron: '1%', |
|||
}, |
|||
{ |
|||
name: 'Ice cream sandwich', |
|||
calories: 237, |
|||
fat: 9.0, |
|||
carbs: 37, |
|||
protein: 4.3, |
|||
sodium: 129, |
|||
calcium: '8%', |
|||
iron: '1%', |
|||
}, |
|||
{ |
|||
name: 'Eclair', |
|||
calories: 262, |
|||
fat: 16.0, |
|||
carbs: 23, |
|||
protein: 6.0, |
|||
sodium: 337, |
|||
calcium: '6%', |
|||
iron: '7%', |
|||
}, |
|||
{ |
|||
name: 'Cupcake', |
|||
calories: 305, |
|||
fat: 3.7, |
|||
carbs: 67, |
|||
protein: 4.3, |
|||
sodium: 413, |
|||
calcium: '3%', |
|||
iron: '8%', |
|||
}, |
|||
{ |
|||
name: 'Gingerbread', |
|||
calories: 356, |
|||
fat: 16.0, |
|||
carbs: 49, |
|||
protein: 3.9, |
|||
sodium: 327, |
|||
calcium: '7%', |
|||
iron: '16%', |
|||
}, |
|||
{ |
|||
name: 'Jelly bean', |
|||
calories: 375, |
|||
fat: 0.0, |
|||
carbs: 94, |
|||
protein: 0.0, |
|||
sodium: 50, |
|||
calcium: '0%', |
|||
iron: '0%', |
|||
}, |
|||
{ |
|||
name: 'Lollipop', |
|||
calories: 392, |
|||
fat: 0.2, |
|||
carbs: 98, |
|||
protein: 0, |
|||
sodium: 38, |
|||
calcium: '0%', |
|||
iron: '2%', |
|||
}, |
|||
{ |
|||
name: 'Honeycomb', |
|||
calories: 408, |
|||
fat: 3.2, |
|||
carbs: 87, |
|||
protein: 6.5, |
|||
sodium: 562, |
|||
calcium: '0%', |
|||
iron: '45%', |
|||
}, |
|||
{ |
|||
name: 'Donut', |
|||
calories: 452, |
|||
fat: 25.0, |
|||
carbs: 51, |
|||
protein: 4.9, |
|||
sodium: 326, |
|||
calcium: '2%', |
|||
iron: '22%', |
|||
}, |
|||
{ |
|||
name: 'KitKat', |
|||
calories: 518, |
|||
fat: 26.0, |
|||
carbs: 65, |
|||
protein: 7, |
|||
sodium: 54, |
|||
calcium: '12%', |
|||
iron: '6%', |
|||
}, |
|||
]; |
|||
|
|||
const separator = ref('vertical'); |
|||
</script> |
|||
|
|||
<style scoped lang="css"> |
|||
:deep(.q-table) { |
|||
border-collapse: collapse; |
|||
} |
|||
.q-table--vertical-separator td, |
|||
.q-table--vertical-separator th, |
|||
.q-table--cell-separator td, |
|||
.q-table--cell-separator th { |
|||
border-bottom-width: 1px !important; |
|||
} |
|||
</style> |
@ -0,0 +1,153 @@ |
|||
<template> |
|||
<div> |
|||
<div class="flex flex-nowrap"> |
|||
<div class="flex-none">这是测试标题</div> |
|||
<div class="flex-1"> |
|||
<w-toolbar :no-icon="toolbar.noIcon" :align="toolbar.align" :x-gap="toolbar.xGap" :buttons="toolbar.buttons"></w-toolbar> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
|
|||
const toolbar = { |
|||
xGap: 8, |
|||
noIcon: false, |
|||
align: 'right', |
|||
buttons: [ |
|||
[ |
|||
{ |
|||
name: 'query', |
|||
icon: 'search', |
|||
label: '查询', |
|||
beforeClick: (tableRef, context) => {}, |
|||
click: (tableRef, context, _click) => { |
|||
console.info('查询'); |
|||
}, |
|||
afterClick: (tableRef, context) => {}, |
|||
}, |
|||
'separator', |
|||
{ |
|||
name: 'moreQuery', |
|||
icon: 'search', |
|||
label: '更多查询', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('更多查询'); |
|||
}, |
|||
}, |
|||
'separator', |
|||
{ |
|||
name: 'reset', |
|||
icon: 'autorenew', |
|||
label: '重置', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('重置'); |
|||
}, |
|||
}, |
|||
], |
|||
'separator', |
|||
{ |
|||
name: 'addnew', |
|||
extend: 'add', |
|||
icon: 'add', |
|||
label: '新增', |
|||
beforeClick: (tableRef, context) => {}, |
|||
click: (tableRef, context, _click) => { |
|||
console.info('新增'); |
|||
}, |
|||
afterClick: (tableRef, context) => {}, |
|||
}, |
|||
'separator', |
|||
[ |
|||
{ |
|||
name: 'edit', |
|||
icon: 'edit', |
|||
label: '编辑', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('编辑'); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'clone', |
|||
icon: 'edit', |
|||
label: '复制', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('复制'); |
|||
}, |
|||
}, |
|||
'separator', |
|||
[ |
|||
{ |
|||
name: 'editConfig', |
|||
icon: 'edit', |
|||
label: '修改配置信息', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('修改配置信息'); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'editBasicInfo', |
|||
icon: 'edit', |
|||
label: '修改基本信息', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('修改基本信息'); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'editVersionInfo', |
|||
icon: 'edit', |
|||
label: '修改版本信息', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('修改版本信息'); |
|||
}, |
|||
}, |
|||
'separator', |
|||
[ |
|||
{ |
|||
name: 'edit1', |
|||
icon: 'edit', |
|||
label: '修改详细信息1', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('修改详细信息1'); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'edit2', |
|||
icon: 'edit', |
|||
label: '修改详细信息2', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('修改详细信息2'); |
|||
}, |
|||
}, |
|||
], |
|||
], |
|||
], |
|||
'separator', |
|||
{ |
|||
name: 'remove', |
|||
icon: 'remove', |
|||
label: '删除', |
|||
click: (tableRef, context, _click) => { |
|||
console.info('删除'); |
|||
}, |
|||
}, |
|||
// { icon: 'home', label: '测试1' }, |
|||
// { separator: true }, |
|||
// { icon: 'home', label: '测试2' }, |
|||
// { icon: 'home', label: '测试3' }, |
|||
// { icon: 'home', label: '测试4' }, |
|||
// { separator: true }, |
|||
// { separator: true }, |
|||
// { icon: 'home', label: '测试5' }, |
|||
// { icon: 'home', label: '测试6' }, |
|||
// { icon: 'home', label: '测试7' }, |
|||
// { icon: 'home', label: '测试8' }, |
|||
// { icon: 'home', label: '测试9' }, |
|||
// { icon: 'home', label: '测试10' }, |
|||
// { icon: 'home', label: '测试11' }, |
|||
// { icon: 'home', label: '测试12' }, |
|||
// { icon: 'home', label: '测试13' }, |
|||
], |
|||
}; |
|||
</script> |
@ -0,0 +1,30 @@ |
|||
<template> |
|||
<div> |
|||
<w-form :fields="fields" :cols-num-auto="false" :cols-num="3"></w-form> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
const fields = [ |
|||
{ |
|||
label: '姓名', |
|||
name: 'name', |
|||
type: 'text', |
|||
required: true, |
|||
colspan: 2, |
|||
button: { |
|||
round: false, |
|||
icon: 'home', |
|||
label: '选择', |
|||
click: () => { |
|||
console.info('11111'); |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
label: '年龄', |
|||
name: 'age', |
|||
type: 'number', |
|||
required: true, |
|||
}, |
|||
]; |
|||
</script> |
@ -0,0 +1,4 @@ |
|||
<template> |
|||
<div>2222</div> |
|||
</template> |
|||
<script setup lang="ts"></script> |
Loading…
Reference in new issue