!36 use unocss

* chore: update deps
* fix: dark
* fix: use table
* feat: 解决Form组件slot必须传递component字段才显示的问题
* style(TableColumn): fix Column settings container is too wide
* fix: eslint
* chore: update deps
* refactor: use setup
* docs: doc
* refactor: use unocss
pull/38/head
xingyu 2023-09-24 12:34:42 +00:00
parent 6b8325c4af
commit 2334fc31ab
58 changed files with 1536 additions and 2355 deletions

View File

@ -6,6 +6,7 @@
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"usernamehw.errorlens",
"mrmlnc.vscode-less", "mrmlnc.vscode-less",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"redhat.vscode-yaml", "redhat.vscode-yaml",

12
.vscode/settings.json vendored
View File

@ -66,6 +66,12 @@
"source.fixAll.eslint": true, "source.fixAll.eslint": true,
"source.organizeImports": false "source.organizeImports": false
}, },
"eslint.rules.customizations": [
{
"rule": "@stylistic/*",
"severity": "off"
}
],
"eslint.validate": [ "eslint.validate": [
"javascript", "javascript",
"javascriptreact", "javascriptreact",
@ -82,7 +88,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
}, },
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
@ -120,6 +126,7 @@
"i18n-ally.enabledFrameworks": ["vue", "react"], "i18n-ally.enabledFrameworks": ["vue", "react"],
"cSpell.words": [ "cSpell.words": [
"antd", "antd",
"antdv",
"antfu", "antfu",
"antv", "antv",
"brotli", "brotli",
@ -166,6 +173,9 @@
"*.env": "$(capture).env.*", "*.env": "$(capture).env.*",
"package.json": ".hintrc,pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore" "package.json": ".hintrc,pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
}, },
"eslint.codeAction.showDocumentation": {
"enable": true
},
"terminal.integrated.scrollback": 10000, "terminal.integrated.scrollback": 10000,
"nuxt.isNuxtApp": false "nuxt.isNuxtApp": false
} }

View File

@ -35,8 +35,8 @@
| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.1.6 | | [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.1.6 |
| [vueuse](https://vueuse.org/) | 常用工具集 | 10.4.1 | | [vueuse](https://vueuse.org/) | 常用工具集 | 10.4.1 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.4.1 | | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.4.1 |
| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.2.4 | | [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.2.5 |
| [unocss](https://uno.antfu.me/) | 原子 css | 0.55.7 | | [unocss](https://uno.antfu.me/) | 原子 css | 0.56.1 |
| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.1 | | [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.1 |
- ![alt VbenAdmin](https://anncwb.github.io/anncwb/images/preview1.png) - ![alt VbenAdmin](https://anncwb.github.io/anncwb/images/preview1.png)

View File

@ -76,7 +76,7 @@
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "^9.4.1", "vue-i18n": "^9.4.1",
"vue-json-pretty": "^2.2.4", "vue-json-pretty": "^2.2.4",
"vue-router": "^4.2.4", "vue-router": "^4.2.5",
"vue-types": "^5.1.1", "vue-types": "^5.1.1",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
@ -84,7 +84,7 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.7.1", "@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0", "@commitlint/config-conventional": "^17.7.0",
"@iconify/json": "^2.2.117", "@iconify/json": "^2.2.119",
"@purge-icons/generated": "^0.9.0", "@purge-icons/generated": "^0.9.0",
"@types/codemirror": "^5.60.10", "@types/codemirror": "^5.60.10",
"@types/crypto-js": "^4.1.2", "@types/crypto-js": "^4.1.2",
@ -92,10 +92,10 @@
"@types/inquirer": "^9.0.3", "@types/inquirer": "^9.0.3",
"@types/lodash-es": "^4.17.9", "@types/lodash-es": "^4.17.9",
"@types/node": "^20.6.0", "@types/node": "^20.6.0",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.1",
"@types/qs": "^6.9.8", "@types/qs": "^6.9.8",
"@types/sortablejs": "^1.15.2", "@types/sortablejs": "^1.15.2",
"@unocss/eslint-config": "^0.56.0", "@unocss/eslint-config": "^0.56.1",
"@vitejs/plugin-vue": "4.3.4", "@vitejs/plugin-vue": "4.3.4",
"@vitejs/plugin-vue-jsx": "^3.0.2", "@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.3.4",
@ -104,7 +104,7 @@
"cz-git": "^1.7.1", "cz-git": "^1.7.1",
"czg": "^1.7.1", "czg": "^1.7.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^8.49.0", "eslint": "^8.50.0",
"esno": "^0.17.0", "esno": "^0.17.0",
"fs-extra": "^11.1.1", "fs-extra": "^11.1.1",
"husky": "^8.0.3", "husky": "^8.0.3",
@ -117,7 +117,7 @@
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",
"rollup": "^3.29.2", "rollup": "^3.29.3",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.9.2",
"stylelint": "^15.10.3", "stylelint": "^15.10.3",
"stylelint-config-recess-order": "^4.3.0", "stylelint-config-recess-order": "^4.3.0",
@ -126,9 +126,9 @@
"stylelint-config-standard": "^34.0.0", "stylelint-config-standard": "^34.0.0",
"stylelint-order": "^6.0.3", "stylelint-order": "^6.0.3",
"stylelint-prettier": "^4.0.2", "stylelint-prettier": "^4.0.2",
"terser": "^5.19.4", "terser": "^5.20.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"unocss": "^0.56.0", "unocss": "^0.56.1",
"vite": "^4.4.9", "vite": "^4.4.9",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mkcert": "^1.16.0", "vite-plugin-mkcert": "^1.16.0",
@ -138,7 +138,7 @@
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-svg-icons": "^2.0.1",
"vite-vue-plugin-html": "^1.0.2", "vite-vue-plugin-html": "^1.0.2",
"vue-eslint-parser": "^9.3.1", "vue-eslint-parser": "^9.3.1",
"vue-tsc": "^1.8.11" "vue-tsc": "^1.8.13"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx}": [

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ const { t } = useI18n()
</script> </script>
<template> <template>
<div :class="`${prefixCls}`"> <div class="relative h-10 flex flex-shrink-0 items-center border-t-1 rounded-bl-2xl px-4 py-0 dark:border-light-100">
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" />
<span>{{ t('component.app.toSearch') }}</span> <span>{{ t('component.app.toSearch') }}</span>
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" /> <AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" />
@ -23,18 +23,6 @@ const { t } = useI18n()
@prefix-cls: ~'@{namespace}-app-search-footer'; @prefix-cls: ~'@{namespace}-app-search-footer';
.@{prefix-cls} { .@{prefix-cls} {
position: relative;
display: flex;
flex-shrink: 0;
align-items: center;
height: 44px;
padding: 0 16px;
font-size: 12px;
// color: var(--text-color);
// background-color: var(--component-background);
border-top: 1px solid var(--border-color);
border-radius: 0 0 16px 16px;
&-item { &-item {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -6,7 +6,6 @@ import { InfoCircleOutlined } from '@ant-design/icons-vue'
import { getPopupContainer } from '@/utils' import { getPopupContainer } from '@/utils'
import { isArray, isString } from '@/utils/is' import { isArray, isString } from '@/utils/is'
import { getSlot } from '@/utils/helper/tsxHelper' import { getSlot } from '@/utils/helper/tsxHelper'
import { useDesign } from '@/hooks/web/useDesign'
const props = { const props = {
/** /**
@ -44,7 +43,6 @@ export default defineComponent({
components: { Tooltip }, components: { Tooltip },
props, props,
setup(props, { slots }) { setup(props, { slots }) {
const { prefixCls } = useDesign('basic-help')
const getTooltipStyle = computed((): CSSProperties => ({ color: props.color, fontSize: props.fontSize })) const getTooltipStyle = computed((): CSSProperties => ({ color: props.color, fontSize: props.fontSize }))
@ -74,14 +72,14 @@ export default defineComponent({
return () => { return () => {
return ( return (
<Tooltip <Tooltip
overlayClassName={`${prefixCls}__wrap`} overlayClassName="overlay-class"
title={<div style={unref(getTooltipStyle)}>{renderTitle()}</div>} title={<div style={unref(getTooltipStyle)}>{renderTitle()}</div>}
autoAdjustOverflow={true} autoAdjustOverflow={true}
overlayStyle={unref(getOverlayStyle)} overlayStyle={unref(getOverlayStyle)}
placement={props.placement as 'right'} placement={props.placement as 'right'}
getPopupContainer={() => getPopupContainer()} getPopupContainer={() => getPopupContainer()}
> >
<span class={prefixCls}>{getSlot(slots) || <InfoCircleOutlined />}</span> <span class="ml-1.5 inline-block cursor-pointer text-sm">{getSlot(slots) || <InfoCircleOutlined />}</span>
</Tooltip> </Tooltip>
) )
} }
@ -89,19 +87,10 @@ export default defineComponent({
}) })
</script> </script>
<style lang="less"> <style>
@prefix-cls: ~'@{namespace}-basic-help'; .overlay-class {
p {
.@{prefix-cls} { margin-bottom: 0;
display: inline-block;
margin-left: 6px;
font-size: 14px;
cursor: pointer;
&__wrap {
p {
margin-bottom: 0;
}
} }
} }
</style> </style>

View File

@ -31,7 +31,7 @@ const props = {
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => { const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
const { item } = props const { item } = props
return ( return (
<span style="display: inline-block; width: 100%; " class="px-4" onClick={props.handler.bind(null, item)}> <span class="inline-block w-full px-4" onClick={props.handler.bind(null, item)}>
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />} {props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
<span>{item.label}</span> <span>{item.label}</span>
</span> </span>

View File

@ -3,7 +3,6 @@ import { ref, watch } from 'vue'
import { Input } from 'ant-design-vue' import { Input } from 'ant-design-vue'
import CronTabModal from './CronTabModal.vue' import CronTabModal from './CronTabModal.vue'
import { cronEmits, cronProps } from './cron.data' import { cronEmits, cronProps } from './cron.data'
import { useDesign } from '@/hooks/web/useDesign'
import { useModal } from '@/components/Modal' import { useModal } from '@/components/Modal'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
@ -14,7 +13,6 @@ const props = defineProps({
exeStartTime: propTypes.oneOfType([propTypes.number, propTypes.string, propTypes.object]).def(0), exeStartTime: propTypes.oneOfType([propTypes.number, propTypes.string, propTypes.object]).def(0),
}) })
const emit = defineEmits([...cronEmits]) const emit = defineEmits([...cronEmits])
const { prefixCls } = useDesign('cron-input')
const [registerModal, { openModal }] = useModal() const [registerModal, { openModal }] = useModal()
const editCronValue = ref(props.value) const editCronValue = ref(props.value)
@ -37,11 +35,11 @@ function showConfigModal() {
</script> </script>
<template> <template>
<div :class="`${prefixCls}`"> <div>
<Input v-model:value="editCronValue" :placeholder="placeholder" :disabled="disabled"> <Input v-model:value="editCronValue" :placeholder="placeholder" :disabled="disabled">
<template #addonAfter> <template #addonAfter>
<a class="open-btn" :disabled="disabled ? 'disabled' : null" @click="showConfigModal"> <a class="cursor-pointer" :disabled="disabled ? 'disabled' : null" @click="showConfigModal">
<Icon icon="ant-design:setting-outlined" /> <Icon class="relative right-0.5 top-0.25" icon="ant-design:setting-outlined" />
<span>选择</span> <span>选择</span>
</a> </a>
</template> </template>
@ -56,19 +54,3 @@ function showConfigModal() {
/> />
</div> </div>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-cron-input';
.@{prefix-cls} {
a.open-btn {
cursor: pointer;
.app-iconify {
position: relative;
top: 1px;
right: 2px;
}
}
}
</style>

View File

@ -1,6 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { computed, ref, unref, watch, watchEffect } from 'vue' import { computed, ref, unref, watch, watchEffect } from 'vue'
import { Avatar } from 'ant-design-vue'
import CopperModal from './CopperModal.vue' import CopperModal from './CopperModal.vue'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useModal } from '@/components/Modal' import { useModal } from '@/components/Modal'
@ -65,9 +66,9 @@ defineExpose({ openModal: openModal.bind(null, true), closeModal })
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
<Icon icon="ant-design:cloud-upload-outlined" :size="getIconWidth" :style="getImageWrapperStyle" color="#d6d6d6" /> <Icon icon="ant-design:cloud-upload-outlined" :size="getIconWidth" :style="getImageWrapperStyle" color="#d6d6d6" />
</div> </div>
<img v-if="sourceValue" :src="sourceValue" alt="avatar"> <Avatar v-if="sourceValue" :size="{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }" :src="sourceValue" alt="avatar" />
</div> </div>
<a-button v-if="showBtn" :class="`${prefixCls}-upload-btn`" v-bind="btnProps" @click="openModal"> <a-button v-if="showBtn" class="mx-auto my-2.5" v-bind="btnProps" @click="openModal">
{{ btnText ? btnText : t('component.cropper.selectImage') }} {{ btnText ? btnText : t('component.cropper.selectImage') }}
</a-button> </a-button>
@ -113,9 +114,5 @@ defineExpose({ openModal: openModal.bind(null, true), closeModal })
&-image-mask:hover { &-image-mask:hover {
opacity: 40; opacity: 40;
} }
&-upload-btn {
margin: 10px auto;
}
} }
</style> </style>

View File

@ -2,7 +2,6 @@
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { computed } from 'vue' import { computed } from 'vue'
import { footerProps } from '../props' import { footerProps } from '../props'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'BasicDrawerFooter' }) defineOptions({ name: 'BasicDrawerFooter' })
@ -15,8 +14,6 @@ const props = defineProps({
}) })
const emit = defineEmits(['ok', 'close']) const emit = defineEmits(['ok', 'close'])
const { prefixCls } = useDesign('basic-drawer-footer')
const getStyle = computed((): CSSProperties => { const getStyle = computed((): CSSProperties => {
const heightStr = `${props.height}` const heightStr = `${props.height}`
return { return {
@ -35,14 +32,21 @@ function handleClose() {
</script> </script>
<template> <template>
<div v-if="showFooter || $slots.footer" :class="prefixCls" :style="getStyle"> <div
v-if="showFooter || $slots.footer"
class="absolute bottom-0 w-full border-t-1 border-light-200 bg-white pl-5 pr-3 dark:bg-dark"
:style="getStyle"
>
<template v-if="!$slots.footer"> <template v-if="!$slots.footer">
<slot name="insertFooter" /> <slot name="insertFooter" />
<a-button v-if="showCancelBtn" v-bind="cancelButtonProps" class="mr-2" @click="handleClose"> <a-button v-if="showCancelBtn" v-bind="cancelButtonProps" class="mr-2" @click="handleClose">
{{ cancelText }} {{ cancelText }}
</a-button> </a-button>
<slot name="centerFooter" /> <slot name="centerFooter" />
<a-button v-if="showOkBtn" :type="okType" v-bind="okButtonProps" class="mr-2" :loading="confirmLoading" @click="handleOk"> <a-button
v-if="showOkBtn" :type="okType" v-bind="okButtonProps" class="mr-2" :loading="confirmLoading"
@click="handleOk"
>
{{ okText }} {{ okText }}
</a-button> </a-button>
<slot name="appendFooter" /> <slot name="appendFooter" />
@ -53,21 +57,3 @@ function handleClose() {
</template> </template>
</div> </div>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-drawer-footer';
@footer-height: 60px;
.@{prefix-cls} {
position: absolute;
bottom: 0;
width: 100%;
padding: 0 12px 0 20px;
text-align: right;
background-color: var(--component-background);
border-top: 1px solid var(--border-color);
> * {
margin-right: 8px;
}
}
</style>

View File

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ArrowLeftOutlined } from '@ant-design/icons-vue' import { ArrowLeftOutlined } from '@ant-design/icons-vue'
import { BasicTitle } from '@/components/Basic' import { BasicTitle } from '@/components/Basic'
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
defineOptions({ name: 'BasicDrawerHeader' }) defineOptions({ name: 'BasicDrawerHeader' })
@ -13,52 +12,27 @@ defineProps({
}) })
const emit = defineEmits(['close']) const emit = defineEmits(['close'])
const { prefixCls } = useDesign('basic-drawer-header')
function handleClose() { function handleClose() {
emit('close') emit('close')
} }
</script> </script>
<template> <template>
<BasicTitle v-if="!isDetail" :class="prefixCls"> <BasicTitle v-if="!isDetail" class="h-full flex items-center">
<slot name="title" /> <slot name="title" />
{{ !$slots.title ? title : '' }} {{ !$slots.title ? title : '' }}
</BasicTitle> </BasicTitle>
<div v-else :class="[prefixCls, `${prefixCls}--detail`]"> <div v-else>
<span :class="`${prefixCls}__twrap`"> <span class="flex-1">
<span v-if="showDetailBack" @click="handleClose"> <span v-if="showDetailBack" @click="handleClose">
<ArrowLeftOutlined :class="`${prefixCls}__back`" /> <ArrowLeftOutlined class="cursor-pointer px-3" />
</span> </span>
<span v-if="title">{{ title }}</span> <span v-if="title">{{ title }}</span>
</span> </span>
<span :class="`${prefixCls}__toolbar`"> <span class="pr-12.5">
<slot name="titleToolbar" /> <slot name="titleToolbar" />
</span> </span>
</div> </div>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-drawer-header';
@footer-height: 60px;
.@{prefix-cls} {
display: flex;
align-items: center;
height: 100%;
&__back {
padding: 0 12px;
cursor: pointer;
}
&__twrap {
flex: 1;
}
&__toolbar {
padding-right: 50px;
}
}
</style>

View File

@ -280,11 +280,11 @@ function getFileName(path) {
<Upload <Upload
:headers="headers" :headers="headers"
:multiple="multiple" :multiple="multiple"
:action="uploadUrl as any" :action="uploadUrl"
:file-list="fileList" :file-list="fileList"
:disabled="disabled" :disabled="disabled"
v-bind="bindProps" v-bind="bindProps"
@remove="onRemove as any" @remove="onRemove"
@change="onFileChange" @change="onFileChange"
@preview="onFilePreview" @preview="onFilePreview"
> >
@ -305,7 +305,6 @@ function getFileName(path) {
</template> </template>
<style lang="less"> <style lang="less">
//noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-upload'; @prefix-cls: ~'@{namespace}-upload';
.@{prefix-cls} { .@{prefix-cls} {
@ -323,12 +322,9 @@ function getFileName(path) {
} }
} }
/* update-begin-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全 */
.upload-download-handler { .upload-download-handler {
right: 6px !important; right: 6px !important;
} }
/* update-end-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全 */
} }
.ant-upload-list-item { .ant-upload-list-item {

View File

@ -140,7 +140,8 @@ export default defineComponent({
const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps
const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') ? rulesMessageJoinLabel : globalRulesMessageJoinLabel const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') ? rulesMessageJoinLabel : globalRulesMessageJoinLabel
const defaultMsg = `${createPlaceholderMessage(component)}${joinLabel ? label : ''}` const assertLabel = joinLabel ? label : ''
const defaultMsg = component ? createPlaceholderMessage(component) + assertLabel : assertLabel
function validator(rule: any, value: any) { function validator(rule: any, value: any) {
const msg = rule.message || defaultMsg const msg = rule.message || defaultMsg
@ -354,8 +355,8 @@ export default defineComponent({
} }
return () => { return () => {
const { colProps = {}, colSlot, renderColContent, component } = props.schema const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema
if (!componentMap.has(component)) if (!componentMap.has(component) && !slot)
return null return null
const { baseColProps = {} } = props.formProps const { baseColProps = {} } = props.formProps

View File

@ -119,7 +119,7 @@ export default defineComponent({
<template> <template>
<div class="v-form-container"> <div class="v-form-container">
<Form ref="eFormModel" class="v-form-model" :model="formModel" v-bind="formModelProps"> <Form ref="eFormModel" class="overflow-hidden" :model="formModel" v-bind="formModelProps">
<Row> <Row>
<FormRender <FormRender
v-for="(schema, index) of noHiddenList" v-for="(schema, index) of noHiddenList"
@ -143,9 +143,3 @@ export default defineComponent({
</Form> </Form>
</div> </div>
</template> </template>
<style lang="less" scoped>
.v-form-model {
overflow: hidden;
}
</style>

View File

@ -1,22 +1,9 @@
<!-- <!--
* @Description: 组件属性控件 * @Description: 组件属性控件
--> -->
<script lang="ts"> <script lang="ts" setup>
import { import { Checkbox, Col, Empty, Form, FormItem, Select } from 'ant-design-vue'
Checkbox, import { computed, ref, watch } from 'vue'
Col,
Empty,
Form,
FormItem,
Input,
InputNumber,
RadioGroup,
Row,
Select,
Switch,
} from 'ant-design-vue'
import RadioButtonGroup from '/@/components/Form/src/components/RadioButtonGroup.vue'
import { computed, defineComponent, ref, watch } from 'vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import { import {
baseComponentAttrs, baseComponentAttrs,
@ -28,145 +15,116 @@ import { formItemsForEach, remove } from '../../../utils'
import type { IBaseFormAttrs } from '../config/formItemPropsConfig' import type { IBaseFormAttrs } from '../config/formItemPropsConfig'
import FormOptions from './FormOptions.vue' import FormOptions from './FormOptions.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'ComponentProps', // compuated
components: { const allOptions = ref([] as Omit<IBaseFormAttrs, 'tag'>[])
FormOptions, function showControlAttrs(includes: string[] | undefined) {
Empty, if (!includes)
Input, return true
Form, return includes.includes(formConfig.value.currentItem!.component)
FormItem, }
Switch,
Checkbox, if (formConfig.value.currentItem) {
Select, formConfig.value.currentItem.componentProps
InputNumber, = formConfig.value.currentItem.componentProps || {}
RadioGroup, }
RadioButtonGroup,
Col, watch(
Row, () => formConfig.value.currentItem?.field,
(_newValue, oldValue) => {
formConfig.value.schemas
&& formItemsForEach(formConfig.value.schemas, (item) => {
if (item.link) {
const index = item.link.findIndex(linkItem => linkItem === oldValue)
index !== -1 && remove(item.link, index)
}
})
}, },
setup() { )
// compuated
const allOptions = ref([] as Omit<IBaseFormAttrs, 'tag'>[]) watch(
const showControlAttrs = (includes: string[] | undefined) => { () => formConfig.value.currentItem && formConfig.value.currentItem.component,
if (!includes) () => {
return true allOptions.value = []
return includes.includes(formConfig.value.currentItem!.component) baseComponentControlAttrs.forEach((item) => {
} item.category = 'control'
if (!item.includes) {
// include
const { formConfig } = useFormDesignState() allOptions.value.push(item)
}
else if (item.includes.includes(formConfig.value.currentItem!.component)) {
// include
allOptions.value.push(item)
}
})
if (formConfig.value.currentItem) { baseComponentCommonAttrs.forEach((item) => {
formConfig.value.currentItem.componentProps item.category = 'input'
= formConfig.value.currentItem.componentProps || {} if (item.includes) {
} if (item.includes.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
watch( }
() => formConfig.value.currentItem?.field, else if (item.exclude) {
(_newValue, oldValue) => { if (!item.exclude.includes(formConfig.value.currentItem!.component))
formConfig.value.schemas allOptions.value.push(item)
&& formItemsForEach(formConfig.value.schemas, (item) => { }
if (item.link) { else {
const index = item.link.findIndex(linkItem => linkItem === oldValue) allOptions.value.push(item)
index !== -1 && remove(item.link, index) }
} })
})
},
)
watch(
() => formConfig.value.currentItem && formConfig.value.currentItem.component,
() => {
allOptions.value = []
baseComponentControlAttrs.forEach((item) => {
item.category = 'control'
if (!item.includes) {
// include
baseComponentAttrs[formConfig.value.currentItem!.component]
&& baseComponentAttrs[formConfig.value.currentItem!.component].forEach(async (item) => {
if (item.component) {
if (['Switch', 'Checkbox', 'Radio'].includes(item.component)) {
item.category = 'control'
allOptions.value.push(item) allOptions.value.push(item)
} }
else if (item.includes.includes(formConfig.value.currentItem!.component)) {
// include
allOptions.value.push(item)
}
})
baseComponentCommonAttrs.forEach((item) => {
item.category = 'input'
if (item.includes) {
if (item.includes.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
}
else if (item.exclude) {
if (!item.exclude.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
}
else { else {
item.category = 'input'
allOptions.value.push(item) allOptions.value.push(item)
} }
}) }
baseComponentAttrs[formConfig.value.currentItem!.component]
&& baseComponentAttrs[formConfig.value.currentItem!.component].forEach(async (item) => {
if (item.component) {
if (['Switch', 'Checkbox', 'Radio'].includes(item.component)) {
item.category = 'control'
allOptions.value.push(item)
}
else {
item.category = 'input'
allOptions.value.push(item)
}
}
})
},
{
immediate: true,
},
)
//
const controlOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'control'
}) })
})
//
const inputOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'input'
})
})
watch(
() => formConfig.value.currentItem!.componentProps,
() => {
const func = componentPropsFuncs[formConfig.value.currentItem!.component]
if (func)
func(formConfig.value.currentItem!.componentProps, allOptions.value)
},
{
immediate: true,
deep: true,
},
)
const linkOptions = computed(() => {
return (
formConfig.value.schemas
&& formConfig.value.schemas
.filter(item => item.key !== formConfig.value.currentItem!.key)
.map(({ label, field }) => ({ label: `${label}/${field}`, value: field }))
)
})
return {
formConfig,
showControlAttrs,
linkOptions,
controlOptions,
inputOptions,
}
}, },
{
immediate: true,
},
)
//
const controlOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'control'
})
})
//
const inputOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'input'
})
})
watch(
() => formConfig.value.currentItem!.componentProps,
() => {
const func = componentPropsFuncs[formConfig.value.currentItem!.component]
if (func)
func(formConfig.value.currentItem!.componentProps, allOptions.value)
},
{
immediate: true,
deep: true,
},
)
const linkOptions = computed(() => {
return (
formConfig.value.schemas
&& formConfig.value.schemas
.filter(item => item.key !== formConfig.value.currentItem!.key)
.map(({ label, field }) => ({ label: `${label}/${field}`, value: field }))
)
}) })
</script> </script>
@ -184,27 +142,20 @@ export default defineComponent({
<div v-if="item.children"> <div v-if="item.children">
<component <component
v-bind="child.componentProps" v-bind="child.componentProps" :is="child.component" v-for="(child, index) of item.children"
:is="child.component" :key="index" v-model:value="formConfig.currentItem.componentProps[item.name][index]"
v-for="(child, index) of item.children"
:key="index"
v-model:value="formConfig.currentItem.componentProps[item.name][index]"
/> />
</div> </div>
<!-- 如果不是数组则正常处理属性值 --> <!-- 如果不是数组则正常处理属性值 -->
<component <component
v-bind="item.componentProps" v-bind="item.componentProps" :is="item.component" v-else
:is="item.component" v-model:value="formConfig.currentItem.componentProps[item.name]" class="component-prop"
v-else
v-model:value="formConfig.currentItem.componentProps[item.name]"
class="component-prop"
/> />
</FormItem> </FormItem>
<FormItem label="控制属性"> <FormItem label="控制属性">
<Col v-for="item in controlOptions" :key="item.name"> <Col v-for="item in controlOptions" :key="item.name">
<Checkbox <Checkbox
v-if="showControlAttrs(item.includes)" v-if="showControlAttrs(item.includes)" v-bind="item.componentProps"
v-bind="item.componentProps"
v-model:checked="formConfig.currentItem.componentProps[item.name]" v-model:checked="formConfig.currentItem.componentProps[item.name]"
> >
{{ item.label }} {{ item.label }}
@ -213,25 +164,19 @@ export default defineComponent({
</FormItem> </FormItem>
</div> </div>
<FormItem label="关联字段"> <FormItem label="关联字段">
<Select <Select v-model:value="formConfig.currentItem.link" mode="multiple" :options="linkOptions" />
v-model:value="formConfig.currentItem.link"
mode="multiple"
:options="linkOptions"
/>
</FormItem> </FormItem>
<FormItem <FormItem
v-if=" v-if="[
[ 'Select',
'Select', 'CheckboxGroup',
'CheckboxGroup', 'RadioGroup',
'RadioGroup', 'TreeSelect',
'TreeSelect', 'Cascader',
'Cascader', 'AutoComplete',
'AutoComplete', ].includes(formConfig.currentItem.component)
].includes(formConfig.currentItem.component) " label="选项"
"
label="选项"
> >
<FormOptions /> <FormOptions />
</FormItem> </FormItem>

View File

@ -1,45 +1,20 @@
<!-- <!--
* @Description: 表单项属性 * @Description: 表单项属性
--> -->
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue' import { Empty, Form, FormItem } from 'ant-design-vue'
import { Checkbox, Empty, Form, FormItem, Input, Select, Slider, Switch } from 'ant-design-vue'
import { isArray } from 'lodash-es' import { isArray } from 'lodash-es'
import { baseItemColumnProps } from '../config/formItemPropsConfig' import { baseItemColumnProps } from '../config/formItemPropsConfig'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import RuleProps from './RuleProps.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'FormItemProps', function showProps(exclude: string[] | undefined) {
components: { if (!exclude)
RuleProps, return true
Empty,
Input,
Form,
FormItem,
Switch,
Checkbox,
Select,
Slider,
},
// props: {} as PropsOptions,
setup() { return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
const { formConfig } = useFormDesignState() }
const showProps = (exclude: string[] | undefined) => {
if (!exclude)
return true
return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
}
return {
baseItemColumnProps,
formConfig,
showProps,
}
},
})
</script> </script>
<template> <template>
@ -50,11 +25,8 @@ export default defineComponent({
<div v-for="item of baseItemColumnProps" :key="item.name"> <div v-for="item of baseItemColumnProps" :key="item.name">
<FormItem v-if="showProps(item.exclude)" :label="item.label"> <FormItem v-if="showProps(item.exclude)" :label="item.label">
<component <component
v-bind="item.componentProps" v-bind="item.componentProps" :is="item.component" v-if="formConfig.currentItem.colProps"
:is="item.component" v-model:value="formConfig.currentItem.colProps[item.name]" class="component-props"
v-if="formConfig.currentItem.colProps"
v-model:value="formConfig.currentItem.colProps[item.name]"
class="component-props"
/> />
</FormItem> </FormItem>
</div> </div>

View File

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 表单项属性控件属性面板 * @Description: 表单项属性控件属性面板
--> -->
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, watch } from 'vue' import { computed, watch } from 'vue'
import { import {
Checkbox, Checkbox,
Col, Col,
@ -10,9 +10,6 @@ import {
Form, Form,
FormItem, FormItem,
Input, Input,
RadioGroup,
Select,
Slider,
Switch, Switch,
} from 'ant-design-vue' } from 'ant-design-vue'
import { isArray } from 'lodash-es' import { isArray } from 'lodash-es'
@ -26,61 +23,32 @@ import {
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import RuleProps from './RuleProps.vue' import RuleProps from './RuleProps.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'FormItemProps',
components: {
RuleProps,
Empty,
Input,
Form,
FormItem,
Switch,
Checkbox,
Select,
Slider,
Col,
RadioGroup,
},
// props: {} as PropsOptions,
setup() { watch(
const { formConfig } = useFormDesignState() () => formConfig.value,
() => {
watch( if (formConfig.value.currentItem) {
() => formConfig.value, formConfig.value.currentItem.itemProps = formConfig.value.currentItem.itemProps || {}
() => { formConfig.value.currentItem.itemProps.labelCol
if (formConfig.value.currentItem) {
formConfig.value.currentItem.itemProps = formConfig.value.currentItem.itemProps || {}
formConfig.value.currentItem.itemProps.labelCol
= formConfig.value.currentItem.itemProps.labelCol || {} = formConfig.value.currentItem.itemProps.labelCol || {}
formConfig.value.currentItem.itemProps.wrapperCol formConfig.value.currentItem.itemProps.wrapperCol
= formConfig.value.currentItem.itemProps.wrapperCol || {} = formConfig.value.currentItem.itemProps.wrapperCol || {}
}
},
{ deep: true, immediate: true },
)
const showProps = (exclude: string[] | undefined) => {
if (!exclude)
return true
return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
}
const controlPropsList = computed(() => {
return baseFormItemControlAttrs.filter((item) => {
return showProps(item.exclude)
})
})
return {
baseFormItemProps,
advanceFormItemProps,
advanceFormItemColProps,
formConfig,
controlPropsList,
showProps,
} }
}, },
{ deep: true, immediate: true },
)
function showProps(exclude: string[] | undefined) {
if (!exclude)
return true
return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
}
const controlPropsList = computed(() => {
return baseFormItemControlAttrs.filter((item) => {
return showProps(item.exclude)
})
}) })
</script> </script>

View File

@ -1,42 +1,29 @@
<!-- <!--
* @Description: 拖拽节点控件 * @Description: 拖拽节点控件
--> -->
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { defineComponent, reactive, toRefs } from 'vue'
import type { IVFormComponent } from '../../../typings/v-form-component' import type { IVFormComponent } from '../../../typings/v-form-component'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import VFormItem from '../../VFormItem/index.vue' import VFormItem from '../../VFormItem/index.vue'
import FormNodeOperate from './FormNodeOperate.vue' import FormNodeOperate from './FormNodeOperate.vue'
// import VFormItem from '../../VFormItem/vFormItem.vue'; const props = defineProps(
export default defineComponent({ {
name: 'FormNode',
components: {
VFormItem,
FormNodeOperate,
},
props: {
schema: { schema: {
type: Object as PropType<IVFormComponent>, type: Object as PropType<IVFormComponent>,
required: true, required: true,
}, },
}, },
setup(props) {
const { formConfig, formDesignMethods } = useFormDesignState() )
const state = reactive({})
// formDesignMethods const { formConfig, formDesignMethods } = useFormDesignState()
const handleSelectItem = () => { // formDesignMethods
// formDesignMethods function handleSelectItem() {
formDesignMethods.handleSetSelectItem(props.schema) // formDesignMethods
} formDesignMethods.handleSetSelectItem(props.schema)
return { }
...toRefs(state),
handleSelectItem,
formConfig,
}
},
})
</script> </script>
<template> <template>

View File

@ -52,10 +52,10 @@ function deleteGridOptions(index: number) {
</div> </div>
<div v-else> <div v-else>
<div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index"> <div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index">
<div class="options-box"> <div class="mb-1.5 flex items-center">
<Input v-model:value="item.label" /> <Input v-model:value="item.label" />
<Input v-model:value="item.value" class="options-value" /> <Input v-model:value="item.value" class="mx-2" />
<a class="options-delete" @click="deleteOptions(index)"> <a class="h-7.5 w-7.5 rounded-full bg-light-50 text-center text-gray-500 hover:bg-red-500" @click="deleteOptions(index)">
<Icon icon="ant-design:delete-outlined" /> <Icon icon="ant-design:delete-outlined" />
</a> </a>
</div> </div>
@ -67,30 +67,3 @@ function deleteGridOptions(index: number) {
</div> </div>
</div> </div>
</template> </template>
<style lang="less" scoped>
.options-box {
display: flex;
align-items: center;
margin-bottom: 5px;
.options-value {
margin: 0 8px;
}
.options-delete {
flex-shrink: 0;
width: 30px;
height: 30px;
line-height: 30px;
color: #666;
text-align: center;
background: #f5f5f5;
border-radius: 50%;
&:hover {
background: #ff4d4f;
}
}
}
</style>

View File

@ -109,7 +109,7 @@ defineExpose({ showModal })
取消 取消
</a-button> </a-button>
<Upload <Upload
class="upload-button" class="mx-2.5"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:show-upload-list="false" :show-upload-list="false"
accept="application/json" accept="application/json"
@ -124,9 +124,3 @@ defineExpose({ showModal })
</template> </template>
</Modal> </Modal>
</template> </template>
<style lang="less" scoped>
.upload-button {
margin: 0 10px;
}
</style>

View File

@ -3,9 +3,9 @@
* 千万不要在template下面的第一行加注释因为这里拖动的第一个元素 * 千万不要在template下面的第一行加注释因为这里拖动的第一个元素
--> -->
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { computed, defineComponent, reactive, toRefs } from 'vue' import { computed } from 'vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { Col, Row } from 'ant-design-vue' import { Col, Row } from 'ant-design-vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
@ -13,16 +13,8 @@ import type { IVFormComponent } from '../../../typings/v-form-component'
import FormNode from './FormNode.vue' import FormNode from './FormNode.vue'
import FormNodeOperate from './FormNodeOperate.vue' import FormNodeOperate from './FormNodeOperate.vue'
export default defineComponent({ const props = defineProps(
name: 'LayoutItem', {
components: {
FormNode,
FormNodeOperate,
Draggable: draggable,
Row,
Col,
},
props: {
schema: { schema: {
type: Object as PropType<IVFormComponent>, type: Object as PropType<IVFormComponent>,
required: true, required: true,
@ -32,33 +24,23 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
emits: ['dragStart', 'handleColAdd', 'handle-copy', 'handle-delete'], )
setup(props) {
const {
formDesignMethods: { handleSetSelectItem },
formConfig,
} = useFormDesignState()
const state = reactive({})
const colPropsComputed = computed(() => {
const { colProps = {} } = props.schema
return colProps
})
const list1 = computed(() => props.schema.columns) const emit = defineEmits(['dragStart', 'handleColAdd', 'handle-copy', 'handle-delete'])
// AColdiv const Draggable = draggable
const layoutTag = computed(() => {
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
})
return { const { formDesignMethods: { handleSetSelectItem }, formConfig } = useFormDesignState()
...toRefs(state), const colPropsComputed = computed(() => {
colPropsComputed, const { colProps = {} } = props.schema
handleSetSelectItem, return colProps
layoutTag, })
list1,
} const list1 = computed(() => props.schema.columns)
},
// AColdiv
const layoutTag = computed(() => {
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
}) })
</script> </script>
@ -87,8 +69,8 @@ export default defineComponent({
class="drag-move" class="drag-move"
:schema="element" :schema="element"
:current-item="currentItem" :current-item="currentItem"
@handle-copy="$emit('handle-copy')" @handle-copy="emit('handle-copy')"
@handle-delete="$emit('handle-delete')" @handle-delete="emit('handle-delete')"
/> />
</template> </template>
</Draggable> </Draggable>
@ -102,8 +84,8 @@ export default defineComponent({
:key="schema.key" :key="schema.key"
:schema="schema" :schema="schema"
:current-item="currentItem" :current-item="currentItem"
@handle-copy="$emit('handle-copy')" @handle-copy="emit('handle-copy')"
@handle-delete="$emit('handle-delete')" @handle-delete="emit('handle-delete')"
/> />
</Col> </Col>
</template> </template>

View File

@ -1,16 +1,12 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs, unref } from 'vue' import { ref, unref } from 'vue'
import { CodeEditor, MODE } from '@/components/CodeEditor' import { CodeEditor, MODE } from '@/components/CodeEditor'
import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard' import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
export default defineComponent({ const props = defineProps(
name: 'PreviewCode', {
components: {
CodeEditor,
},
props: {
fileFormat: { fileFormat: {
type: String, type: String,
default: 'json', default: 'json',
@ -20,51 +16,37 @@ export default defineComponent({
default: '', default: '',
}, },
}, },
setup(props) { )
const state = reactive({
open: false,
})
const myEditor = ref(null) const myEditor = ref(null)
const exportData = (data: string, fileName = `file.${props.fileFormat}`) => { function exportData(data: string, fileName = `file.${props.fileFormat}`) {
let content = 'data:text/csv;charset=utf-8,' let content = 'data:text/csv;charset=utf-8,'
content += data content += data
const encodedUri = encodeURI(content) const encodedUri = encodeURI(content)
const actions = document.createElement('a') const actions = document.createElement('a')
actions.setAttribute('href', encodedUri) actions.setAttribute('href', encodedUri)
actions.setAttribute('download', fileName) actions.setAttribute('download', fileName)
actions.click() actions.click()
} }
const handleExportJson = () => { function handleExportJson() {
exportData(props.editorJson) exportData(props.editorJson)
} }
const { clipboardRef, copiedRef } = useCopyToClipboard() const { clipboardRef, copiedRef } = useCopyToClipboard()
const { createMessage } = useMessage() const { createMessage } = useMessage()
const handleCopyJson = () => { function handleCopyJson() {
// //
const value = props.editorJson const value = props.editorJson
if (!value) { if (!value) {
createMessage.warning('代码为空!') createMessage.warning('代码为空!')
return return
} }
clipboardRef.value = value clipboardRef.value = value
if (unref(copiedRef)) if (unref(copiedRef))
createMessage.warning('复制成功!') createMessage.warning('复制成功!')
} }
return {
...toRefs(state),
myEditor,
exportData,
handleCopyJson,
handleExportJson,
MODE,
}
},
})
</script> </script>
<template> <template>
@ -72,10 +54,10 @@ export default defineComponent({
<div class="v-json-box"> <div class="v-json-box">
<CodeEditor ref="myEditor" :value="editorJson" :mode="MODE.JSON" /> <CodeEditor ref="myEditor" :value="editorJson" :mode="MODE.JSON" />
</div> </div>
<div class="copy-btn-box"> <div class="pt-2 text-center">
<a-button <a-button
type="primary" type="primary"
class="copy-btn" class="mr-2"
data-clipboard-action="copy" data-clipboard-action="copy"
:data-clipboard-text="editorJson" :data-clipboard-text="editorJson"
@click="handleCopyJson" @click="handleCopyJson"
@ -88,15 +70,3 @@ export default defineComponent({
</div> </div>
</div> </div>
</template> </template>
<style lang="less" scoped>
// modal
.copy-btn-box {
padding-top: 8px;
text-align: center;
.copy-btn {
margin-right: 8px;
}
}
</style>

View File

@ -19,7 +19,6 @@ import PropsPanel from './modules/PropsPanel.vue'
import ImportJsonModal from './components/ImportJsonModal.vue' import ImportJsonModal from './components/ImportJsonModal.vue'
import CodeModal from './components/CodeModal.vue' import CodeModal from './components/CodeModal.vue'
import { globalConfigState } from './config/formItemPropsConfig' import { globalConfigState } from './config/formItemPropsConfig'
import { useDesign } from '@/hooks/web/useDesign'
import { CollapseContainer } from '@/components/Container/index' import { CollapseContainer } from '@/components/Container/index'
import 'codemirror/mode/javascript/javascript' import 'codemirror/mode/javascript/javascript'
@ -29,7 +28,6 @@ defineProps({
default: 'v-form-antd表单设计器', default: 'v-form-antd表单设计器',
}, },
}) })
const { prefixCls } = useDesign('form-design')
// //
const propsPanel = ref<null | IPropsPanel>(null) const propsPanel = ref<null | IPropsPanel>(null)
const jsonModal = ref<null | IToolbarMethods>(null) const jsonModal = ref<null | IToolbarMethods>(null)
@ -251,7 +249,7 @@ provide<IFormDesignMethods>('formDesignMethods', {
<template> <template>
<Layout> <Layout>
<LayoutSider <LayoutSider
:class="`left ${prefixCls}-sider`" class="bg-white dark:bg-black"
collapsible collapsible
collapsed-width="0" collapsed-width="0"
width="270" width="270"
@ -302,7 +300,7 @@ provide<IFormDesignMethods>('formDesignMethods', {
/> />
</LayoutContent> </LayoutContent>
<LayoutSider <LayoutSider
:class="`right ${prefixCls}-sider`" class="bg-white dark:bg-black"
collapsible collapsible
:reverse-arrow="true" :reverse-arrow="true"
collapsed-width="0" collapsed-width="0"
@ -327,19 +325,3 @@ provide<IFormDesignMethods>('formDesignMethods', {
<VFormPreview ref="eFormPreview" :form-config="formConfig" /> <VFormPreview ref="eFormPreview" :form-config="formConfig" />
<VFormPreview2 ref="eFormPreview2" :form-config="formConfig" /> <VFormPreview2 ref="eFormPreview2" :form-config="formConfig" />
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-form-design';
[data-theme='dark'] {
.@{prefix-cls}-sider {
background-color: #1f1f1f;
}
}
[data-theme='light'] {
.@{prefix-cls}-sider {
background-color: #fff;
}
}
</style>

View File

@ -1,16 +1,13 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive } from 'vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import type { IVFormComponent } from '../../../typings/v-form-component' import type { IVFormComponent } from '../../../typings/v-form-component'
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
export default defineComponent({ const props = defineProps(
name: 'CollapseItem', {
components: { Draggable: draggable, Icon },
props: {
list: { list: {
type: [Array], type: Array as unknown as any[],
default: () => [], default: () => [],
}, },
handleListPush: { handleListPush: {
@ -18,24 +15,23 @@ export default defineComponent({
default: null, default: null,
}, },
}, },
setup(props, { emit }) { )
const { prefixCls } = useDesign('form-design-collapse-item') const emit = defineEmits(['start', 'add-attrs', 'handle-list-push'])
const Draggable = draggable
const state = reactive({}) const { prefixCls } = useDesign('form-design-collapse-item')
const handleStart = (e: any, list1: IVFormComponent[]) => {
emit('start', list1[e.oldIndex].component) function handleStart(e: any, list1: IVFormComponent[]) {
} emit('start', list1[e.oldIndex].component)
const handleAdd = (e: any) => { }
console.log(e) function handleAdd(e: any) {
} console.log(e)
// https://github.com/SortableJS/vue.draggable.next }
// https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/custom-clone.vue // https://github.com/SortableJS/vue.draggable.next
const cloneItem = (one) => { // https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/custom-clone.vue
return props.handleListPush(one) function cloneItem(one) {
} return props.handleListPush(one)
return { prefixCls, state, handleStart, handleAdd, cloneItem } }
},
})
</script> </script>
<template> <template>
@ -57,8 +53,8 @@ export default defineComponent({
<template #item="{ element, index }"> <template #item="{ element, index }">
<li <li
class="bs-box text-ellipsis" class="bs-box text-ellipsis"
@dragstart="$emit('add-attrs', list, index)" @dragstart="emit('add-attrs', list, index)"
@click="$emit('handle-list-push', element)" @click="emit('handle-list-push', element)"
> >
<!-- <svg v-if="element.icon.indexOf('icon-') > -1" class="icon" aria-hidden="true"> <!-- <svg v-if="element.icon.indexOf('icon-') > -1" class="icon" aria-hidden="true">
<use :xlink:href="`#${element.icon}`" /> <use :xlink:href="`#${element.icon}`" />

View File

@ -2,89 +2,61 @@
* @Description: 中间表单布局面板 * @Description: 中间表单布局面板
* https://github.com/SortableJS/vue.draggable.next/issues/138 * https://github.com/SortableJS/vue.draggable.next/issues/138
--> -->
<script lang="ts"> <script lang="ts" setup>
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { computed, defineComponent } from 'vue' import { computed } from 'vue'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { Empty, Form } from 'ant-design-vue' import { Empty, Form } from 'ant-design-vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import LayoutItem from '../components/LayoutItem.vue' import LayoutItem from '../components/LayoutItem.vue'
export default defineComponent({ const emit = defineEmits(['handleSetSelectItem'])
name: 'FormComponentPanel',
components: {
LayoutItem,
Draggable: draggable,
Form,
Empty,
},
emits: ['handleSetSelectItem'],
setup(_, { emit }) {
const { formConfig } = useFormDesignState()
/** const Draggable = draggable
* 拖拽完成事件
* @param newIndex
*/
const addItem = ({ newIndex }: any) => {
formConfig.value.schemas = formConfig.value.schemas || []
const schemas = formConfig.value.schemas const { formConfig } = useFormDesignState()
schemas[newIndex] = cloneDeep(schemas[newIndex])
emit('handleSetSelectItem', schemas[newIndex])
}
/** /**
* 拖拽开始事件 * 拖拽完成事件
* @param e {Object} 事件对象 * @param newIndex
*/ */
const handleDragStart = (e: any) => { function addItem({ newIndex }: any) {
emit('handleSetSelectItem', formConfig.value.schemas[e.oldIndex]) formConfig.value.schemas = formConfig.value.schemas || []
}
// currentItem const schemas = formConfig.value.schemas
schemas[newIndex] = cloneDeep(schemas[newIndex])
emit('handleSetSelectItem', schemas[newIndex])
}
// AColdiv /**
const layoutTag = computed(() => { * 拖拽开始事件
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div' * @param e {Object} 事件对象
}) */
function handleDragStart(e: any) {
emit('handleSetSelectItem', formConfig.value.schemas[e.oldIndex])
}
return { // currentItem
addItem,
handleDragStart, // AColdiv
formConfig, const layoutTag = computed(() => {
layoutTag, return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
}
},
}) })
</script> </script>
<template> <template>
<div class="v-form-container form-panel"> <div class="v-form-container form-panel">
<Empty <Empty v-show="formConfig.schemas.length === 0" class="empty-text" description="从左侧选择控件添加" />
v-show="formConfig.schemas.length === 0"
class="empty-text"
description="从左侧选择控件添加"
/>
<Form v-bind="formConfig"> <Form v-bind="formConfig">
<div class="draggable-box"> <div class="draggable-box">
<Draggable <Draggable
v-model="formConfig.schemas" v-model="formConfig.schemas" class="list-main ant-row" group="form-draggable"
class="list-main ant-row" :component-data="{ name: 'list', tag: 'div', type: 'transition-group' }" ghost-class="moving" :animation="180"
group="form-draggable" handle=".drag-move" item-key="key" @add="addItem" @start="handleDragStart"
:component-data="{ name: 'list', tag: 'div', type: 'transition-group' }"
ghost-class="moving"
:animation="180"
handle=".drag-move"
item-key="key"
@add="addItem"
@start="handleDragStart"
> >
<template #item="{ element }"> <template #item="{ element }">
<LayoutItem <LayoutItem
class="drag-move" class="drag-move" :schema="element" :data="formConfig"
:schema="element"
:data="formConfig"
:current-item="formConfig.currentItem || {}" :current-item="formConfig.currentItem || {}"
/> />
</template> </template>
@ -95,72 +67,74 @@ export default defineComponent({
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
@import url('../styles/variable.less'); @import url('../styles/variable.less');
@import url('../styles/drag.less'); @import url('../styles/drag.less');
.v-form-container { .v-form-container {
//
.ant-form-inline {
.list-main {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
justify-content: flex-start;
.layout-width { //
width: 100%; .ant-form-inline {
} .list-main {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
justify-content: flex-start;
.layout-width {
width: 100%;
}
}
.ant-form-item-control-wrapper {
min-width: 175px !important;
}
}
}
.form-panel {
position: relative;
height: 100%;
.empty-text {
position: absolute;
inset: -10% 0 0;
z-index: 100;
height: 150px;
margin: auto;
color: #aaa;
}
.draggable-box {
height: calc(100vh - 200px);
// width: 100%;
overflow: auto;
.drag-move {
min-height: 62px;
cursor: move;
}
.list-main {
//
.list-enter-active {
transition: all 0.5s;
} }
.ant-form-item-control-wrapper { .list-leave-active {
min-width: 175px !important; transition: all 0.3s;
} }
}
} .list-enter,
.list-leave-to {
.form-panel { opacity: 0;
position: relative; transform: translateX(-100px);
height: 100%; }
.empty-text { .list-enter {
position: absolute; height: 30px;
inset: -10% 0 0;
z-index: 100;
height: 150px;
margin: auto;
color: #aaa;
}
.draggable-box {
height: calc(100vh - 200px);
// width: 100%;
overflow: auto;
.drag-move {
min-height: 62px;
cursor: move;
}
.list-main {
//
.list-enter-active {
transition: all 0.5s;
}
.list-leave-active {
transition: all 0.3s;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateX(-100px);
}
.list-enter {
height: 30px;
}
} }
} }
} }
}
</style> </style>

View File

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 右侧属性配置面板 * @Description: 右侧属性配置面板
--> -->
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent } from 'vue' import { computed } from 'vue'
import { TabPane, Tabs } from 'ant-design-vue' import { TabPane, Tabs } from 'ant-design-vue'
import FormProps from '../components/FormProps.vue' import FormProps from '../components/FormProps.vue'
import FormItemProps from '../components/FormItemProps.vue' import FormItemProps from '../components/FormItemProps.vue'
@ -15,25 +15,11 @@ type ChangeTabKey = 1 | 2
export interface IPropsPanel { export interface IPropsPanel {
changeTab: (key: ChangeTabKey) => void changeTab: (key: ChangeTabKey) => void
} }
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'PropsPanel', const slotProps = computed(() => {
components: { return customComponents.find(
FormProps, item => item.component === formConfig.value.currentItem?.component,
FormItemProps, )
ComponentProps,
ComponentColumnProps,
Tabs,
TabPane,
},
setup() {
const { formConfig } = useFormDesignState()
const slotProps = computed(() => {
return customComponents.find(
item => item.component === formConfig.value.currentItem?.component,
)
})
return { formConfig, customComponents, slotProps }
},
}) })
</script> </script>

View File

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 工具栏 * @Description: 工具栏
--> -->
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, inject, reactive, toRefs } from 'vue' import { inject, reactive } from 'vue'
import type { UseRefHistoryReturn } from '@vueuse/core' import type { UseRefHistoryReturn } from '@vueuse/core'
import { Divider, Tooltip } from 'ant-design-vue' import { Divider, Tooltip } from 'ant-design-vue'
import type { IFormConfig } from '../../../typings/v-form-component' import type { IFormConfig } from '../../../typings/v-form-component'
@ -15,62 +15,51 @@ interface IToolbarsConfig {
event: string event: string
} }
export default defineComponent({ const state = reactive<{
name: 'OperatingArea', toolbarsConfigs: IToolbarsConfig[]
components: { }>({
Tooltip, toolbarsConfigs: [
Icon, {
Divider, title: '预览-支持布局',
}, type: 'preview',
setup() { event: 'handlePreview',
const state = reactive<{ icon: 'ant-design:chrome-filled',
toolbarsConfigs: IToolbarsConfig[] },
}>({ {
toolbarsConfigs: [ title: '预览-不支持布局',
{ type: 'preview',
title: '预览-支持布局', event: 'handlePreview2',
type: 'preview', icon: 'ant-design:chrome-filled',
event: 'handlePreview', },
icon: 'ant-design:chrome-filled', {
}, title: '导入JSON',
{ type: 'importJson',
title: '预览-不支持布局', event: 'handleOpenImportJsonModal',
type: 'preview', icon: 'ant-design:import-outlined',
event: 'handlePreview2', },
icon: 'ant-design:chrome-filled', {
}, title: '生成JSON',
{ type: 'exportJson',
title: '导入JSON', event: 'handleOpenJsonModal',
type: 'importJson', icon: 'ant-design:export-outlined',
event: 'handleOpenImportJsonModal', },
icon: 'ant-design:import-outlined', {
}, title: '生成代码',
{ type: 'exportCode',
title: '生成JSON', event: 'handleOpenCodeModal',
type: 'exportJson', icon: 'ant-design:code-filled',
event: 'handleOpenJsonModal', },
icon: 'ant-design:export-outlined', {
}, title: '清空',
{ type: 'reset',
title: '生成代码', event: 'handleClearFormItems',
type: 'exportCode', icon: 'ant-design:clear-outlined',
event: 'handleOpenCodeModal', },
icon: 'ant-design:code-filled', ],
},
{
title: '清空',
type: 'reset',
event: 'handleClearFormItems',
icon: 'ant-design:clear-outlined',
},
],
})
const historyRef = inject('historyReturn') as UseRefHistoryReturn<IFormConfig, IFormConfig>
const { undo, redo, canUndo, canRedo } = historyRef
return { ...toRefs(state), undo, redo, canUndo, canRedo }
},
}) })
const historyRef = inject('historyReturn') as UseRefHistoryReturn<IFormConfig, IFormConfig>
const { undo, redo, canUndo, canRedo } = historyRef
</script> </script>
<template> <template>
@ -78,7 +67,7 @@ export default defineComponent({
<!-- 头部操作按钮区域 start --> <!-- 头部操作按钮区域 start -->
<!-- 操作左侧区域 start --> <!-- 操作左侧区域 start -->
<div class="left-btn-box"> <div class="left-btn-box">
<Tooltip v-for="item in toolbarsConfigs" :key="item.icon" :title="item.title"> <Tooltip v-for="item in state.toolbarsConfigs" :key="item.icon" :title="item.title">
<a class="toolbar-text" @click="$emit(item.event)"> <a class="toolbar-text" @click="$emit(item.event)">
<Icon :icon="item.icon" /> <Icon :icon="item.icon" />
</a> </a>

View File

@ -70,7 +70,7 @@
&-close { &-close {
width: auto !important; width: auto !important;
font-weight: normal; font-weight: normal;
background: rgb(255 255 255/ 0%) !important; background: transparent !important;
outline: none; outline: none;
} }

View File

@ -9,8 +9,8 @@ const { getCalcContentWidth } = useMenuSetting()
</script> </script>
<template> <template>
<div :class="prefixCls" :style="{ width: getCalcContentWidth }"> <div class="fixed bottom-0 right-0 z-99 w-full flex items-center border-t-1 px-6 text-base/44" :style="{ width: getCalcContentWidth }">
<div :class="`${prefixCls}__left`"> <div class="flex-1-1">
<slot name="left" /> <slot name="left" />
</div> </div>
<slot /> <slot />
@ -24,24 +24,10 @@ const { getCalcContentWidth } = useMenuSetting()
@prefix-cls: ~'@{namespace}-page-footer'; @prefix-cls: ~'@{namespace}-page-footer';
.@{prefix-cls} { .@{prefix-cls} {
position: fixed;
right: 0;
bottom: 0;
z-index: @page-footer-z-index;
display: flex;
align-items: center;
width: 100%;
padding: 0 24px;
line-height: 44px;
border-top: 1px solid var(--border-color);
box-shadow: box-shadow:
0 -6px 16px -8px rgb(0 0 0 / 8%), 0 -6px 16px -8px rgb(0 0 0 / 8%),
0 -9px 28px 0 rgb(0 0 0 / 5%), 0 -9px 28px 0 rgb(0 0 0 / 5%),
0 -12px 48px 16px rgb(0 0 0 / 3%); 0 -12px 48px 16px rgb(0 0 0 / 3%);
transition: width 0.2s; transition: width 0.2s;
&__left {
flex: 1 1;
}
} }
</style> </style>

View File

@ -316,7 +316,7 @@ emit('register', tableAction, formActions)
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-table'; @prefix-cls: ~'@{namespace}-basic-table';
[data-theme='dark'] { html[data-theme='dark'] {
.ant-table-tbody > tr:hover.ant-table-row-selected > td, .ant-table-tbody > tr:hover.ant-table-row-selected > td,
.ant-table-tbody > tr.ant-table-row-selected td { .ant-table-tbody > tr.ant-table-row-selected td {
background-color: #262626; background-color: #262626;

View File

@ -438,6 +438,7 @@ function updateSortOption(column: BasicColumn) {
} }
.ant-checkbox-group { .ant-checkbox-group {
display: inline-block;
width: 100%; width: 100%;
min-width: 260px; min-width: 260px;
// flex-wrap: wrap; // flex-wrap: wrap;

View File

@ -1,12 +1,13 @@
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import { unref } from 'vue' import { unref } from 'vue'
import type { Key } from 'ant-design-vue/lib/table/interface'
import type { BasicTableProps } from '../types/table' import type { BasicTableProps } from '../types/table'
import { ROW_KEY } from '../const' import { ROW_KEY } from '../const'
import { isFunction, isString } from '@/utils/is' import { isFunction, isString } from '@/utils/is'
interface Options { interface Options {
setSelectedRowKeys: (keys: string[]) => void setSelectedRowKeys: (keys: Key[]) => void
getSelectRowKeys: () => string[] getSelectRowKeys: () => Key[]
clearSelectedRowKeys: () => void clearSelectedRowKeys: () => void
emit: EmitType emit: EmitType
getAutoCreateKey: ComputedRef<boolean | undefined> getAutoCreateKey: ComputedRef<boolean | undefined>

View File

@ -163,8 +163,8 @@ export function useTable(tableProps?: Props): [
scrollTo: (pos: string) => { scrollTo: (pos: string) => {
getTableInstance().scrollTo(pos) getTableInstance().scrollTo(pos)
}, },
setShowForm: async (show: boolean) => { setShowForm: async (flag: boolean) => {
await getTableInstance().setShowForm(show) await getTableInstance().setShowForm(flag)
}, },
getShowForm: () => { getShowForm: () => {
return toRaw(getTableInstance().getShowForm()) return toRaw(getTableInstance().getShowForm())

View File

@ -86,7 +86,7 @@ export interface TableActionType {
expandRows: (keys: (string | number)[]) => void expandRows: (keys: (string | number)[]) => void
collapseAll: () => void collapseAll: () => void
scrollTo: (pos: string) => void // pos: id | "top" | "bottom" scrollTo: (pos: string) => void // pos: id | "top" | "bottom"
getSelectRowKeys: () => string[] getSelectRowKeys: () => Key[]
deleteSelectRowByKey: (key: string) => void deleteSelectRowByKey: (key: string) => void
setPagination: (info: Partial<PaginationProps>) => void setPagination: (info: Partial<PaginationProps>) => void
setTableData: <T extends Ref<Recordable<any>[]>>(values: T[]) => void setTableData: <T extends Ref<Recordable<any>[]>>(values: T[]) => void
@ -108,11 +108,11 @@ export interface TableActionType {
getCacheColumns: () => BasicColumn[] getCacheColumns: () => BasicColumn[]
emit?: EmitType emit?: EmitType
updateTableData: (index: number, key: string, value: any) => Recordable updateTableData: (index: number, key: string, value: any) => Recordable
setShowPagination: (show: boolean) => Promise<void> setShowPagination: (show: boolean) => void
getShowPagination: () => boolean getShowPagination: () => boolean
setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void
setCacheColumns?: (columns: BasicColumn[]) => void setCacheColumns?: (columns: BasicColumn[]) => void
setShowForm: (show: boolean) => Promise<void> setShowForm: (flag: boolean) => void
getShowForm: () => boolean getShowForm: () => boolean
} }
@ -403,6 +403,7 @@ export interface BasicTableProps<T = any> {
export type CellFormat = string | ((text: string, record: Recordable, index: number) => string | number) | Map<string | number, any> export type CellFormat = string | ((text: string, record: Recordable, index: number) => string | number) | Map<string | number, any>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
export interface BasicColumn extends ColumnProps<Recordable> { export interface BasicColumn extends ColumnProps<Recordable> {
children?: BasicColumn[] children?: BasicColumn[]

View File

@ -1,7 +1,6 @@
@import "transition/index.less"; @import "transition/index.less";
@import "var/index.less"; @import "var/index.less";
@import "public.less"; @import "public.less";
// @import "ant/index.less";
@import "./theme.less"; @import "./theme.less";
@import "./entry.css"; @import "./entry.css";

View File

@ -11,7 +11,6 @@ import { openWindow } from '@/utils'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useRootSetting } from '@/hooks/setting/useRootSetting' import { useRootSetting } from '@/hooks/setting/useRootSetting'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'LayoutFooter' }) defineOptions({ name: 'LayoutFooter' })
@ -22,7 +21,6 @@ const Footer = Layout.Footer
const { t } = useI18n() const { t } = useI18n()
const { getShowFooter } = useRootSetting() const { getShowFooter } = useRootSetting()
const { currentRoute } = useRouter() const { currentRoute } = useRouter()
const { prefixCls } = useDesign('layout-footer')
const footerRef = ref<ComponentRef>(null) const footerRef = ref<ComponentRef>(null)
const { setFooterHeight } = useLayoutHeight() const { setFooterHeight } = useLayoutHeight()
@ -40,43 +38,15 @@ const getShowLayoutFooter = computed(() => {
</script> </script>
<template> <template>
<Footer v-if="getShowLayoutFooter" ref="footerRef" :class="prefixCls"> <Footer v-if="getShowLayoutFooter" ref="footerRef" class="text-center text-[var(--normal-text)]">
<div :class="`${prefixCls}__links`"> <div class="mb-2">
<a @click="openWindow(SITE_URL)"></a> <a class="text-[var(--normal-text)] hover:text-[var(--hover-text)]" @click="openWindow(SITE_URL)"></a>
<GithubFilled :class="`${prefixCls}__github`" @click="openWindow(GITHUB_URL)" /> <GithubFilled class="mx-7.5 hover:text-[var(--hover-text)]" @click="openWindow(GITHUB_URL)" />
<a @click="openWindow(DOC_URL)">{{ t('layout.footer.onlineDocument') }}</a> <a class="text-[var(--normal-text)] hover:text-[var(--hover-text)]" @click="openWindow(DOC_URL)">{{
t('layout.footer.onlineDocument') }}</a>
</div> </div>
<div>Copyright &copy;2023 {{ SITE_TITLE }}</div> <div>Copyright &copy;2023 {{ SITE_TITLE }}</div>
</Footer> </Footer>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-layout-footer';
.@{prefix-cls} {
color: var(--normal-text);
text-align: center;
&__links {
margin-bottom: 8px;
a {
color: var(--normal-text);
&:hover {
color: var(--hover-text);
}
}
}
&__github {
margin: 0 30px;
&:hover {
color: var(--hover-text);
}
}
}
</style>

View File

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign'
import { BasicForm, useForm } from '@/components/Form' import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal' import { BasicModal, useModalInner } from '@/components/Modal'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
@ -11,7 +10,6 @@ import headerImg from '@/assets/images/header.jpg'
defineOptions({ name: 'LockModal' }) defineOptions({ name: 'LockModal' })
const { t } = useI18n() const { t } = useI18n()
const { prefixCls } = useDesign('header-lock-modal')
const userStore = useUserStore() const userStore = useUserStore()
const lockStore = useLockStore() const lockStore = useLockStore()
@ -52,18 +50,18 @@ const avatar = computed(() => {
</script> </script>
<template> <template>
<BasicModal :footer="null" width="25%" :title="t('layout.header.lockScreen')" v-bind="$attrs" :class="prefixCls" @register="register"> <BasicModal :footer="null" width="25%" :title="t('layout.header.lockScreen')" v-bind="$attrs" @register="register">
<div :class="`${prefixCls}__entry`"> <div class="relative rounded-10 px-8 pb-8 pt-30">
<div :class="`${prefixCls}__header`"> <div class="absolute left-[calc(50%-45px)] top-0 w-auto text-center">
<img :src="avatar" :class="`${prefixCls}__header-img`"> <img :src="avatar" class="w-18 rounded-50%">
<p :class="`${prefixCls}__header-name`"> <p class="mt-2">
{{ getRealName }} {{ getRealName }}
</p> </p>
</div> </div>
<BasicForm @register="registerForm" /> <BasicForm @register="registerForm" />
<div :class="`${prefixCls}__footer`"> <div class="mt-4 text-center">
<a-button type="primary" block class="mt-2" @click="handleLock"> <a-button type="primary" block class="mt-2" @click="handleLock">
{{ t('layout.header.lockScreenBtn') }} {{ t('layout.header.lockScreenBtn') }}
</a-button> </a-button>
@ -71,38 +69,3 @@ const avatar = computed(() => {
</div> </div>
</BasicModal> </BasicModal>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-header-lock-modal';
.@{prefix-cls} {
&__entry {
position: relative;
//height: 240px;
padding: 130px 30px 30px;
border-radius: 10px;
}
&__header {
position: absolute;
top: 0;
left: calc(50% - 45px);
width: auto;
text-align: center;
&-img {
width: 70px;
border-radius: 50%;
}
&-name {
margin-top: 5px;
}
}
&__footer {
margin-top: 16px;
text-align: center;
}
}
</style>

View File

@ -2,7 +2,6 @@
import { computed, ref, unref, watch } from 'vue' import { computed, ref, unref, watch } from 'vue'
import { Avatar, List, Tag, Typography } from 'ant-design-vue' import { Avatar, List, Tag, Typography } from 'ant-design-vue'
import type { ListItem } from './data' import type { ListItem } from './data'
import { useDesign } from '@/hooks/web/useDesign'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
const props = defineProps({ const props = defineProps({
@ -31,7 +30,6 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['update:currentPage']) const emit = defineEmits(['update:currentPage'])
const { prefixCls } = useDesign('header-notify-list')
const current = ref(props.currentPage || 1) const current = ref(props.currentPage || 1)
const getData = computed(() => { const getData = computed(() => {
const { pageSize, list } = props const { pageSize, list } = props
@ -72,12 +70,12 @@ function handleTitleClick(item: ListItem) {
</script> </script>
<template> <template>
<List :class="prefixCls" bordered :pagination="getPagination"> <List class="display-none" bordered :pagination="getPagination">
<template v-for="item in getData" :key="item.id"> <template v-for="item in getData" :key="item.id">
<List.Item class="list-item"> <List.Item class="cursor-pointer overflow-hidden p-1.5 transition-all delay-300">
<List.Item.Meta> <List.Item.Meta>
<template #title> <template #title>
<div class="title"> <div class="mb-2 font-normal">
<Typography.Paragraph <Typography.Paragraph
style="width: 100%; margin-bottom: 0 !important" style="width: 100%; margin-bottom: 0 !important"
:style="{ cursor: isTitleClickable ? 'pointer' : '' }" :style="{ cursor: isTitleClickable ? 'pointer' : '' }"
@ -86,8 +84,8 @@ function handleTitleClick(item: ListItem) {
:content="item.title" :content="item.title"
@click="handleTitleClick(item)" @click="handleTitleClick(item)"
/> />
<div v-if="item.extra" class="extra"> <div v-if="item.extra" class="float-right mr-0 font-normal -mt-0.375">
<Tag class="tag" :color="item.color"> <Tag class="mr-0" :color="item.color">
{{ item.extra }} {{ item.extra }}
</Tag> </Tag>
</div> </div>
@ -95,20 +93,20 @@ function handleTitleClick(item: ListItem) {
</template> </template>
<template #avatar> <template #avatar>
<Avatar v-if="item.avatar" class="avatar" :src="item.avatar" /> <Avatar v-if="item.avatar" class="mt-1" :src="item.avatar" />
<span v-else> {{ item.avatar }}</span> <span v-else> {{ item.avatar }}</span>
</template> </template>
<template #description> <template #description>
<div> <div>
<div v-if="item.description" class="description"> <div v-if="item.description" class="text-xs/18">
<Typography.Paragraph <Typography.Paragraph
style="width: 100%; margin-bottom: 0 !important" style="width: 100%; margin-bottom: 0 !important"
:ellipsis="$props.descRows && $props.descRows > 0 ? { rows: $props.descRows, tooltip: !!item.description } : false" :ellipsis="$props.descRows && $props.descRows > 0 ? { rows: $props.descRows, tooltip: !!item.description } : false"
:content="item.description" :content="item.description"
/> />
</div> </div>
<div class="datetime"> <div class="mt-1 text-xs/18">
{{ item.datetime }} {{ item.datetime }}
</div> </div>
</div> </div>
@ -118,55 +116,3 @@ function handleTitleClick(item: ListItem) {
</template> </template>
</List> </List>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-header-notify-list';
.@{prefix-cls} {
&::-webkit-scrollbar {
display: none;
}
::v-deep(.ant-pagination-disabled) {
display: inline-block !important;
}
&-item {
padding: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s;
.title {
margin-bottom: 8px;
font-weight: normal;
.extra {
float: right;
margin-top: -1.5px;
margin-right: 0;
font-weight: normal;
.tag {
margin-right: 0;
}
}
.avatar {
margin-top: 4px;
}
.description {
font-size: 12px;
line-height: 18px;
}
.datetime {
margin-top: 4px;
font-size: 12px;
line-height: 18px;
}
}
}
}
</style>

View File

@ -33,5 +33,3 @@ onMounted(async () => {
</Tooltip> </Tooltip>
</div> </div>
</template> </template>
<style lang="less"></style>

View File

@ -9,7 +9,6 @@ import LayoutSideBar from './sider/index.vue'
import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting' import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting'
import { useMenuSetting } from '@/hooks/setting/useMenuSetting' import { useMenuSetting } from '@/hooks/setting/useMenuSetting'
import { useAppInject } from '@/hooks/web/useAppInject' import { useAppInject } from '@/hooks/web/useAppInject'
import { useDesign } from '@/hooks/web/useDesign'
import { useLockPage } from '@/hooks/web/useLockPage' import { useLockPage } from '@/hooks/web/useLockPage'
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent' import { createAsyncComponent } from '@/utils/factory/createAsyncComponent'
@ -18,7 +17,6 @@ defineOptions({ name: 'DefaultLayout' })
const LayoutFeatures = createAsyncComponent(() => import('@/layouts/default/feature/index.vue')) const LayoutFeatures = createAsyncComponent(() => import('@/layouts/default/feature/index.vue'))
const LayoutFooter = createAsyncComponent(() => import('@/layouts/default/footer/index.vue')) const LayoutFooter = createAsyncComponent(() => import('@/layouts/default/footer/index.vue'))
const { prefixCls } = useDesign('default-layout')
const { getIsMobile } = useAppInject() const { getIsMobile } = useAppInject()
const { getShowFullHeaderRef } = useHeaderSetting() const { getShowFullHeaderRef } = useHeaderSetting()
const { getShowSidebar, getIsMixSidebar, getShowMenu } = useMenuSetting() const { getShowSidebar, getIsMixSidebar, getShowMenu } = useMenuSetting()
@ -36,12 +34,12 @@ const layoutClass = computed(() => {
</script> </script>
<template> <template>
<Layout :class="prefixCls" v-bind="lockEvents"> <Layout class="min-h-full w-full flex flex-col" v-bind="lockEvents">
<LayoutFeatures /> <LayoutFeatures />
<LayoutHeader v-if="getShowFullHeaderRef" fixed /> <LayoutHeader v-if="getShowFullHeaderRef" fixed />
<Layout :class="[layoutClass]"> <Layout :class="[layoutClass]">
<LayoutSideBar v-if="getShowSidebar || getIsMobile" /> <LayoutSideBar v-if="getShowSidebar || getIsMobile" />
<Layout :class="`${prefixCls}-main`"> <Layout class="ml-0.25 w-full">
<LayoutMultipleHeader /> <LayoutMultipleHeader />
<LayoutContent /> <LayoutContent />
<LayoutFooter /> <LayoutFooter />
@ -49,19 +47,3 @@ const layoutClass = computed(() => {
</Layout> </Layout>
</Layout> </Layout>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-default-layout';
.@{prefix-cls} {
display: flex;
flex-direction: column;
width: 100%;
min-height: 100%;
&-main {
width: 100%;
margin-left: 1px;
}
}
</style>

View File

@ -2,7 +2,6 @@
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue'
import { baseHandler } from '../handler' import { baseHandler } from '../handler'
import type { HandlerEnum } from '../enum' import type { HandlerEnum } from '../enum'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'InputNumberItem' }) defineOptions({ name: 'InputNumberItem' })
@ -14,7 +13,6 @@ const props = defineProps({
type: String, type: String,
}, },
}) })
const { prefixCls } = useDesign('setting-input-number-item')
function handleChange(e) { function handleChange(e) {
props.event && baseHandler(props.event, e) props.event && baseHandler(props.event, e)
@ -22,22 +20,8 @@ function handleChange(e) {
</script> </script>
<template> <template>
<div :class="prefixCls"> <div class="my-4 flex justify-between">
<span> {{ title }}</span> <span> {{ title }}</span>
<InputNumber v-bind="$attrs" :class="`${prefixCls}-input-number`" @change="handleChange" /> <InputNumber v-bind="$attrs" class="max-w-32 w-30" @change="handleChange" />
</div> </div>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-setting-input-number-item';
.@{prefix-cls} {
display: flex;
justify-content: space-between;
margin: 16px 0;
&-input-number {
width: 126px !important;
}
}
</style>

View File

@ -4,7 +4,6 @@ import { computed } from 'vue'
import { Select } from 'ant-design-vue' import { Select } from 'ant-design-vue'
import { baseHandler } from '../handler' import { baseHandler } from '../handler'
import type { HandlerEnum } from '../enum' import type { HandlerEnum } from '../enum'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'SelectItem' }) defineOptions({ name: 'SelectItem' })
@ -29,33 +28,19 @@ const props = defineProps({
default: () => [], default: () => [],
}, },
}) })
const { prefixCls } = useDesign('setting-select-item')
const getBindValue = computed(() => { const getBindValue = computed(() => {
return props.def ? { value: props.def, defaultValue: props.initValue || props.def } : {} return props.def ? { value: props.def, defaultValue: props.initValue || props.def } : {}
}) })
function handleChange(e: ChangeEvent) { function handleChange(e) {
props.event && baseHandler(props.event, e) props.event && baseHandler(props.event, e)
} }
</script> </script>
<template> <template>
<div :class="prefixCls"> <div class="my-4 flex justify-between">
<span> {{ title }}</span> <span> {{ title }}</span>
<Select v-bind="getBindValue" :class="`${prefixCls}-select`" :disabled="disabled" :options="options" @change="handleChange as any" /> <Select v-bind="getBindValue" class="max-w-32 w-30" :disabled="disabled" :options="options" @change="handleChange" />
</div> </div>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-setting-select-item';
.@{prefix-cls} {
display: flex;
justify-content: space-between;
margin: 16px 0;
&-select {
width: 126px;
}
}
</style>

View File

@ -8,7 +8,6 @@ import { usePermissionStore } from '@/store/modules/permission'
import { useMultipleTabStore } from '@/store/modules/multipleTab' import { useMultipleTabStore } from '@/store/modules/multipleTab'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard' import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard'
@ -22,7 +21,6 @@ import { updateSidebarBgColor } from '@/logics/theme/updateBackground'
defineOptions({ name: 'SettingFooter' }) defineOptions({ name: 'SettingFooter' })
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const { prefixCls } = useDesign('setting-footer')
const { t } = useI18n() const { t } = useI18n()
const { createSuccessModal, createMessage } = useMessage() const { createSuccessModal, createMessage } = useMessage()
const tabStore = useMultipleTabStore() const tabStore = useMultipleTabStore()
@ -63,7 +61,7 @@ function handleClearAndRedo() {
</script> </script>
<template> <template>
<div :class="prefixCls"> <div class="flex flex-col items-center">
<a-button type="primary" block @click="handleCopy"> <a-button type="primary" block @click="handleCopy">
<CopyOutlined class="mr-2" /> <CopyOutlined class="mr-2" />
{{ t('layout.setting.copyBtn') }} {{ t('layout.setting.copyBtn') }}
@ -80,13 +78,3 @@ function handleClearAndRedo() {
</a-button> </a-button>
</div> </div>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-setting-footer';
.@{prefix-cls} {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View File

@ -4,7 +4,6 @@ import { computed } from 'vue'
import { Switch } from 'ant-design-vue' import { Switch } from 'ant-design-vue'
import { baseHandler } from '../handler' import { baseHandler } from '../handler'
import type { HandlerEnum } from '../enum' import type { HandlerEnum } from '../enum'
import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'SwitchItem' }) defineOptions({ name: 'SwitchItem' })
@ -23,7 +22,6 @@ const props = defineProps({
type: Boolean, type: Boolean,
}, },
}) })
const { prefixCls } = useDesign('setting-switch-item')
const { t } = useI18n() const { t } = useI18n()
const getBindValue = computed(() => { const getBindValue = computed(() => {
@ -35,7 +33,7 @@ function handleChange(e) {
</script> </script>
<template> <template>
<div :class="prefixCls"> <div class="my-4 flex justify-between">
<span> {{ title }}</span> <span> {{ title }}</span>
<Switch <Switch
v-bind="getBindValue" v-bind="getBindValue"
@ -46,13 +44,3 @@ function handleChange(e) {
/> />
</div> </div>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-setting-switch-item';
.@{prefix-cls} {
display: flex;
justify-content: space-between;
margin: 16px 0;
}
</style>

View File

@ -1,7 +1,8 @@
<script lang="ts"> <script lang="ts" setup>
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { computed, defineComponent, onMounted, ref, unref, watch } from 'vue' import { computed, onMounted, ref, unref, watch } from 'vue'
import type { RouteLocationNormalized } from 'vue-router' import type { RouteLocationNormalized } from 'vue-router'
import { onClickOutside } from '@vueuse/core'
import LayoutTrigger from '../trigger/index.vue' import LayoutTrigger from '../trigger/index.vue'
import { useDragLine } from './useLayoutSider' import { useDragLine } from './useLayoutSider'
import type { Menu } from '@/router/types' import type { Menu } from '@/router/types'
@ -16,257 +17,220 @@ import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useGo } from '@/hooks/web/usePage' import { useGo } from '@/hooks/web/usePage'
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum' import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum'
import clickOutside from '@/directives/clickOutside'
import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '@/router/menus' import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '@/router/menus'
import { listenerRouteChange } from '@/logics/mitt/routeChange' import { listenerRouteChange } from '@/logics/mitt/routeChange'
export default defineComponent({ const wrap = ref(null)
name: 'LayoutMixSider', const menuModules = ref<Menu[]>([])
components: { const activePath = ref('')
ScrollContainer, const childrenMenus = ref<Menu[]>([])
AppLogo, const openMenu = ref(false)
SimpleMenu, const dragBarRef = ref<ElRef>(null)
Icon, const sideRef = ref<ElRef>(null)
LayoutTrigger, const currentRoute = ref<Nullable<RouteLocationNormalized>>(null)
SimpleMenuTag,
const { prefixCls } = useDesign('layout-mix-sider')
const go = useGo()
const { t } = useI18n()
const {
getMenuWidth,
getCanDrag,
getCloseMixSidebarOnChange,
getMenuTheme,
getMixSideTrigger,
getRealWidth,
getMixSideFixed,
mixSideHasChildren,
setMenuSetting,
getIsMixSidebar,
getCollapsed,
} = useMenuSetting()
const { title } = useGlobSetting()
const permissionStore = usePermissionStore()
useDragLine(sideRef, dragBarRef, true)
const getMenuStyle = computed((): CSSProperties => {
return {
width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
// eslint-disable-next-line @typescript-eslint/no-use-before-define
left: `${unref(getMixSideWidth)}px`,
}
})
const getIsFixed = computed(() => {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
mixSideHasChildren.value = unref(childrenMenus).length > 0
const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren)
if (isFixed)
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
openMenu.value = true
return isFixed
})
const getMixSideWidth = computed(() => {
return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH
})
const getDomStyle = computed((): CSSProperties => {
const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0
const width = `${unref(getMixSideWidth) + fixedWidth}px`
return getWrapCommonStyle(width)
})
const getWrapStyle = computed((): CSSProperties => {
const width = `${unref(getMixSideWidth)}px`
return getWrapCommonStyle(width)
})
const getMenuEvents = computed(() => {
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
setActive(true)
closeMenu()
},
}
: {}
})
const getShowDragBar = computed(() => unref(getCanDrag))
onMounted(async () => {
menuModules.value = await getShallowMenus()
})
// Menu changes
watch(
[() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
async () => {
menuModules.value = await getShallowMenus()
}, },
directives: { {
clickOutside, immediate: true,
}, },
setup() { )
const menuModules = ref<Menu[]>([])
const activePath = ref('')
const childrenMenus = ref<Menu[]>([])
const openMenu = ref(false)
const dragBarRef = ref<ElRef>(null)
const sideRef = ref<ElRef>(null)
const currentRoute = ref<Nullable<RouteLocationNormalized>>(null)
const { prefixCls } = useDesign('layout-mix-sider') listenerRouteChange((route) => {
const go = useGo() currentRoute.value = route
const { t } = useI18n() setActive(true)
const { if (unref(getCloseMixSidebarOnChange))
getMenuWidth, closeMenu()
getCanDrag, })
getCloseMixSidebarOnChange,
getMenuTheme,
getMixSideTrigger,
getRealWidth,
getMixSideFixed,
mixSideHasChildren,
setMenuSetting,
getIsMixSidebar,
getCollapsed,
} = useMenuSetting()
const { title } = useGlobSetting() function getWrapCommonStyle(width: string): CSSProperties {
const permissionStore = usePermissionStore() return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
}
}
useDragLine(sideRef, dragBarRef, true) // Process module menu click
async function handleModuleClick(path: string, hover = false) {
const getMenuStyle = computed((): CSSProperties => { const children = await getChildrenMenus(path)
return { if (unref(activePath) === path) {
width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0, if (!hover) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define if (!unref(openMenu))
left: `${unref(getMixSideWidth)}px`,
}
})
const getIsFixed = computed(() => {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
mixSideHasChildren.value = unref(childrenMenus).length > 0
const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren)
if (isFixed)
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
openMenu.value = true openMenu.value = true
else
return isFixed
})
const getMixSideWidth = computed(() => {
return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH
})
const getDomStyle = computed((): CSSProperties => {
const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0
const width = `${unref(getMixSideWidth) + fixedWidth}px`
return getWrapCommonStyle(width)
})
const getWrapStyle = computed((): CSSProperties => {
const width = `${unref(getMixSideWidth)}px`
return getWrapCommonStyle(width)
})
const getMenuEvents = computed(() => {
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
setActive(true)
closeMenu()
},
}
: {}
})
const getShowDragBar = computed(() => unref(getCanDrag))
onMounted(async () => {
menuModules.value = await getShallowMenus()
})
// Menu changes
watch(
[() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
async () => {
menuModules.value = await getShallowMenus()
},
{
immediate: true,
},
)
listenerRouteChange((route) => {
currentRoute.value = route
setActive(true)
if (unref(getCloseMixSidebarOnChange))
closeMenu() closeMenu()
})
function getWrapCommonStyle(width: string): CSSProperties {
return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
}
} }
else {
// Process module menu click if (!unref(openMenu))
async function handleModuleClick(path: string, hover = false) {
const children = await getChildrenMenus(path)
if (unref(activePath) === path) {
if (!hover) {
if (!unref(openMenu))
openMenu.value = true
else
closeMenu()
}
else {
if (!unref(openMenu))
openMenu.value = true
}
if (!unref(openMenu))
setActive()
}
else {
openMenu.value = true openMenu.value = true
activePath.value = path
}
if (!children || children.length === 0) {
if (!hover)
go(path)
childrenMenus.value = []
closeMenu()
return
}
childrenMenus.value = children
} }
if (!unref(openMenu))
setActive()
}
else {
openMenu.value = true
activePath.value = path
}
// Set the currently active menu and submenu if (!children || children.length === 0) {
async function setActive(setChildren = false) { if (!hover)
const path = currentRoute.value?.path
if (!path)
return
activePath.value = await getCurrentParentPath(path)
// hanldeModuleClick(parentPath);
if (unref(getIsMixSidebar)) {
const activeMenu = unref(menuModules).find(item => item.path === unref(activePath))
const p = activeMenu?.path
if (p) {
const children = await getChildrenMenus(p)
if (setChildren) {
childrenMenus.value = children
if (unref(getMixSideFixed))
openMenu.value = children.length > 0
}
if (children.length === 0)
childrenMenus.value = []
}
}
}
function handleMenuClick(path: string) {
go(path) go(path)
} childrenMenus.value = []
closeMenu()
return
}
childrenMenus.value = children
}
function handleClickOutside() { // Set the currently active menu and submenu
setActive(true) async function setActive(setChildren = false) {
closeMenu() const path = currentRoute.value?.path
} if (!path)
return
activePath.value = await getCurrentParentPath(path)
// hanldeModuleClick(parentPath);
if (unref(getIsMixSidebar)) {
const activeMenu = unref(menuModules).find(item => item.path === unref(activePath))
const p = activeMenu?.path
if (p) {
const children = await getChildrenMenus(p)
if (setChildren) {
childrenMenus.value = children
function getItemEvents(item: Menu) { if (unref(getMixSideFixed))
if (unref(getMixSideTrigger) === 'hover') { openMenu.value = children.length > 0
return {
onMouseenter: () => handleModuleClick(item.path, true),
onClick: async () => {
const children = await getChildrenMenus(item.path)
if (item.path && (!children || children.length === 0))
go(item.path)
},
}
}
return {
onClick: () => handleModuleClick(item.path),
} }
if (children.length === 0)
childrenMenus.value = []
} }
}
}
function handleFixedMenu() { function handleMenuClick(path: string) {
setMenuSetting({ go(path)
mixSideFixed: !unref(getIsFixed), }
})
}
// Close menu function handleClickOutside() {
function closeMenu() { setActive(true)
if (!unref(getIsFixed)) closeMenu()
openMenu.value = false }
}
function getItemEvents(item: Menu) {
if (unref(getMixSideTrigger) === 'hover') {
return { return {
t, onMouseenter: () => handleModuleClick(item.path, true),
prefixCls, onClick: async () => {
menuModules, const children = await getChildrenMenus(item.path)
handleModuleClick, if (item.path && (!children || children.length === 0))
activePath, go(item.path)
childrenMenus, },
getShowDragBar,
handleMenuClick,
getMenuStyle,
handleClickOutside,
sideRef,
dragBarRef,
title,
openMenu,
getMenuTheme,
getItemEvents,
getMenuEvents,
getDomStyle,
handleFixedMenu,
getMixSideFixed,
getWrapStyle,
getCollapsed,
} }
}, }
return {
onClick: () => handleModuleClick(item.path),
}
}
function handleFixedMenu() {
setMenuSetting({
mixSideFixed: !unref(getIsFixed),
})
}
// Close menu
function closeMenu() {
if (!unref(getIsFixed))
openMenu.value = false
}
onClickOutside(wrap, () => {
handleClickOutside()
}) })
</script> </script>
<template> <template>
<div :class="`${prefixCls}-dom`" :style="getDomStyle" /> <div :class="`${prefixCls}-dom`" :style="getDomStyle" />
<div <div
v-click-outside="handleClickOutside" ref="wrap"
:style="getWrapStyle" :style="getWrapStyle"
:class="[ :class="[
prefixCls, prefixCls,

View File

@ -26,7 +26,7 @@ const title = computed(() => globSetting?.title ?? '')
</script> </script>
<template> <template>
<div :class="prefixCls" class="relative h-full w-full px-4"> <div :class="prefixCls" class="relative h-full min-h-full w-full overflow-hidden px-4">
<div class="absolute right-4 top-4 flex items-center"> <div class="absolute right-4 top-4 flex items-center">
<AppDarkModeToggle v-if="!sessionTimeout" class="enter-x mr-2" /> <AppDarkModeToggle v-if="!sessionTimeout" class="enter-x mr-2" />
<AppLocalePicker v-if="!sessionTimeout && showLocale" class="enter-x xl:text-gray-600" :show-text="false" /> <AppLocalePicker v-if="!sessionTimeout && showLocale" class="enter-x xl:text-gray-600" :show-text="false" />
@ -108,9 +108,6 @@ html[data-theme='dark'] {
} }
.@{prefix-cls} { .@{prefix-cls} {
min-height: 100%;
overflow: hidden;
@media (max-width: @screen-xl) { @media (max-width: @screen-xl) {
background-color: #293146; background-color: #293146;

View File

@ -1,13 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue' import { onBeforeUnmount, onMounted, ref } from 'vue'
import Login from './Login.vue' import Login from './Login.vue'
import { useDesign } from '@/hooks/web/useDesign'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { PermissionModeEnum } from '@/enums/appEnum' import { PermissionModeEnum } from '@/enums/appEnum'
const { prefixCls } = useDesign('st-login')
const userStore = useUserStore() const userStore = useUserStore()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const appStore = useAppStore() const appStore = useAppStore()
@ -36,20 +34,8 @@ onBeforeUnmount(() => {
<template> <template>
<transition> <transition>
<div :class="prefixCls"> <div class="fixed z-9999999 h-full w-full bg-[var(--component-background)]">
<Login session-timeout /> <Login session-timeout />
</div> </div>
</transition> </transition>
</template> </template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-st-login';
.@{prefix-cls} {
position: fixed;
z-index: 9999999;
width: 100%;
height: 100%;
background: var(--component-background);
}
</style>

View File

@ -32,11 +32,11 @@ onMounted(async () => {
<List.Item> <List.Item>
<List.Item.Meta> <List.Item.Meta>
<template #avatar> <template #avatar>
<Icon v-if="item.avatar" class="avatar" :icon="item.avatar" :color="item.color" /> <Icon v-if="item.avatar" class="text-4xl" :icon="item.avatar" :color="item.color" />
</template> </template>
<template #title> <template #title>
{{ item.title }} {{ item.title }}
<a-button v-if="item.extra" type="link" size="small" class="extra"> <a-button v-if="item.extra" type="link" size="small" class="float-right mr-7.5 mt-2.5 cursor-pointer">
{{ item.extra }} {{ item.extra }}
</a-button> </a-button>
</template> </template>
@ -49,16 +49,3 @@ onMounted(async () => {
</List> </List>
</CollapseContainer> </CollapseContainer>
</template> </template>
<style lang="less" scoped>
.avatar {
font-size: 40px !important;
}
.extra {
float: right;
margin-top: 10px;
margin-right: 30px;
cursor: pointer;
}
</style>

View File

@ -54,7 +54,7 @@ async function handleSubmit() {
<BasicForm @register="register" /> <BasicForm @register="register" />
</Col> </Col>
<Col :span="10"> <Col :span="10">
<div class="change-avatar"> <div>
<div class="mb-2"> <div class="mb-2">
头像 头像
</div> </div>
@ -63,6 +63,7 @@ async function handleSubmit() {
btn-text="更换头像" btn-text="更换头像"
:btn-props="{ preIcon: 'ant-design:cloud-upload-outlined' }" :btn-props="{ preIcon: 'ant-design:cloud-upload-outlined' }"
width="150" width="150"
class="mb-4 block rounded-full"
@change="updateAvatar" @change="updateAvatar"
/> />
</div> </div>
@ -73,13 +74,3 @@ async function handleSubmit() {
</Button> </Button>
</CollapseContainer> </CollapseContainer>
</template> </template>
<style lang="less" scoped>
.change-avatar {
img {
display: block;
margin-bottom: 15px;
border-radius: 50%;
}
}
</style>

View File

@ -15,7 +15,7 @@ const ListItemMeta = List.Item.Meta
<ListItemMeta> <ListItemMeta>
<template #title> <template #title>
{{ item.title }} {{ item.title }}
<Switch class="extra" checked-children="" un-checked-children="" default-checked /> <Switch class="float-right mr-7.5 mt-0" checked-children="" un-checked-children="" default-checked />
</template> </template>
<template #description> <template #description>
<div>{{ item.description }}</div> <div>{{ item.description }}</div>
@ -26,11 +26,3 @@ const ListItemMeta = List.Item.Meta
</List> </List>
</CollapseContainer> </CollapseContainer>
</template> </template>
<style lang="less" scoped>
.extra {
float: right;
margin-top: 10px;
margin-right: 30px;
}
</style>

View File

@ -29,7 +29,7 @@ function handleSuccess() {
<ListItemMeta> <ListItemMeta>
<template #title> <template #title>
{{ item.title }} {{ item.title }}
<div v-if="item.extra" class="extra"> <div v-if="item.extra" class="float-right mr-7.5 mt-2.5 cursor-pointer font-normal text-blue-500">
<a-button type="link" @click="handleEdit(item.title)"> <a-button type="link" @click="handleEdit(item.title)">
{{ item.extra }} {{ item.extra }}
</a-button> </a-button>
@ -45,14 +45,3 @@ function handleSuccess() {
</CollapseContainer> </CollapseContainer>
<PasswordModal @register="registerModal" @success="handleSuccess" /> <PasswordModal @register="registerModal" @success="handleSuccess" />
</template> </template>
<style lang="less" scoped>
.extra {
float: right;
margin-top: 10px;
margin-right: 30px;
font-weight: normal;
color: #1890ff;
cursor: pointer;
}
</style>

View File

@ -15,7 +15,7 @@ const tabBarStyle = { width: '220px' }
<template> <template>
<ScrollContainer> <ScrollContainer>
<div ref="wrapperRef" class="account-setting"> <div ref="wrapperRef" class="m-3 rounded-1.5 bg-[var(--component-background)]">
<Tabs tab-position="left" :tab-bar-style="tabBarStyle"> <Tabs tab-position="left" :tab-bar-style="tabBarStyle">
<template v-for="item in settingList" :key="item.key"> <template v-for="item in settingList" :key="item.key">
<TabPane :tab="item.name"> <TabPane :tab="item.name">
@ -29,19 +29,3 @@ const tabBarStyle = { width: '220px' }
</div> </div>
</ScrollContainer> </ScrollContainer>
</template> </template>
<style lang="less">
.account-setting {
margin: 12px;
background-color: var(--component-background);
border-radius: 6px;
.base-title {
padding-left: 0;
}
// .ant-tabs-tab-active {
// background-color: @item-active-bg;
// }
}
</style>

View File

@ -78,7 +78,7 @@ async function submitForm(formData) {
<template> <template>
<PageWrapper> <PageWrapper>
<div class="step-form-form"> <div class="mx-auto my-0 mt-2.5 w-200">
<Steps :current="current"> <Steps :current="current">
<Steps.Step title="选择流程" /> <Steps.Step title="选择流程" />
<Steps.Step title="流程提交" /> <Steps.Step title="流程提交" />
@ -113,11 +113,3 @@ async function submitForm(formData) {
</div> </div>
</PageWrapper> </PageWrapper>
</template> </template>
<style lang="less" scoped>
.step-form-form {
width: 750px;
margin: 0 auto;
margin-top: 10px;
}
</style>

View File

@ -73,7 +73,7 @@ onMounted(async () => {
<template> <template>
<PageWrapper> <PageWrapper>
<div class="step-form-form"> <div class="mx-auto my-0 mt-2.5 w-200">
<Steps :current="current"> <Steps :current="current">
<Step title="生成信息" /> <Step title="生成信息" />
<Step title="字段信息" /> <Step title="字段信息" />
@ -94,11 +94,3 @@ onMounted(async () => {
</div> </div>
</PageWrapper> </PageWrapper>
</template> </template>
<style lang="less" scoped>
.step-form-form {
width: 750px;
margin: 0 auto;
margin-top: 10px;
}
</style>

View File

@ -51,46 +51,20 @@ watch(
</script> </script>
<template> <template>
<div class="step1"> <div>
<div class="step1-form"> <div class="mx-auto my-0 w-80%">
<BasicForm @register="register" /> <BasicForm @register="register" />
</div> </div>
<Divider /> <Divider />
<h3>说明</h3> <h3 class="mb-3 text-base">
<h4>基本信息</h4> 说明
</h3>
<h4 class="mb-1 text-sm">
基本信息
</h4>
<p> 配置生成的基本信息 </p> <p> 配置生成的基本信息 </p>
<h4>生成信息</h4> <h4>生成信息</h4>
<p> 配置生成生成的详细信息 </p> <p> 配置生成生成的详细信息 </p>
</div> </div>
</template> </template>
<style lang="less" scoped>
.step1 {
&-form {
width: 80%;
margin: 0 auto;
}
h3 {
margin: 0 0 12px;
font-size: 16px;
line-height: 32px;
}
h4 {
margin: 0 0 4px;
font-size: 14px;
line-height: 22px;
}
}
.pay-select {
width: 20%;
}
.pay-input {
width: 70%;
}
</style>

View File

@ -39,11 +39,11 @@ function handleEdit(record: EditRecordRow) {
<template> <template>
<div class="step2"> <div class="step2">
<div class="step2-table"> <div class="mx-auto my-0 w-full">
<BasicTable :data-source="columnsInfo" @register="registerTable" @row-click="handleEdit" /> <BasicTable :data-source="columnsInfo" @register="registerTable" @row-click="handleEdit" />
</div> </div>
<Divider /> <Divider />
<div class="step2-button"> <div class="flex justify-center">
<a-button @click="customResetFunc"> <a-button @click="customResetFunc">
上一步 上一步
</a-button> </a-button>
@ -51,35 +51,12 @@ function handleEdit(record: EditRecordRow) {
提交 提交
</a-button> </a-button>
</div> </div>
<h3>说明</h3> <h3 class="mb-3 text-base">
<h4>配置字段</h4> 说明
</h3>
<h4 class="mb-1 text-sm">
配置字段
</h4>
<p> 配置表的字段类型增删改查字典等 </p> <p> 配置表的字段类型增删改查字典等 </p>
</div> </div>
</template> </template>
<style lang="less" scoped>
.step2 {
&-table {
width: 100%;
margin: 0 auto;
}
&-button {
display: flex;
justify-content: center;
}
h3 {
margin: 0 0 12px;
font-size: 16px;
line-height: 32px;
}
h4 {
margin: 0 0 4px;
font-size: 14px;
line-height: 22px;
}
}
</style>

View File

@ -36,7 +36,7 @@ function handleGoList() {
</script> </script>
<template> <template>
<div class="result-success m-5"> <div class="m-5 bg-white px-8 py-12 dark:bg-dark">
<Result status="success" title="代码生成成功" sub-title=""> <Result status="success" title="代码生成成功" sub-title="">
<template #extra> <template #extra>
<a-button key="console" type="primary" @click="handleGoList"> <a-button key="console" type="primary" @click="handleGoList">
@ -53,15 +53,3 @@ function handleGoList() {
<PreviewModal @register="registerPreviewModal" /> <PreviewModal @register="registerPreviewModal" />
</div> </div>
</template> </template>
<style lang="less" scoped>
.result-success {
padding: 48px 32px;
background-color: var(--component-background);
&__content {
padding: 24px 40px;
background-color: @background-color-light;
}
}
</style>