feat: 初始化项目结构并添加基础配置
添加前后端基础项目结构,包括.gitignore、package.json等配置文件 实现前端基础功能模块,包括路由、状态管理、API请求封装等 添加前端UI组件库和样式体系 配置开发环境Mock系统和构建工具链
This commit is contained in:
86
front-end/src/views/vab/calendar/index.vue
Normal file
86
front-end/src/views/vab/calendar/index.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="calendar-container">
|
||||
<el-calendar ref="calendar">
|
||||
<template #header="{ date }">
|
||||
<span>
|
||||
{{ date }}
|
||||
{{
|
||||
solar2lunar(dayjs(calendar.date).format('YYYY-MM-DD'))
|
||||
.gzYear
|
||||
}}年
|
||||
{{
|
||||
solar2lunar(dayjs(calendar.date).format('YYYY-MM-DD'))
|
||||
.gzMonth
|
||||
}}月
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button @click="selectDate('prev-year')">
|
||||
上一年
|
||||
</el-button>
|
||||
<el-button @click="selectDate('prev-month')">
|
||||
上一月
|
||||
</el-button>
|
||||
<el-button @click="selectDate('today')">今天</el-button>
|
||||
<el-button @click="selectDate('next-month')">
|
||||
下一月
|
||||
</el-button>
|
||||
<el-button @click="selectDate('next-year')">
|
||||
下一年
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
<template #dateCell="{ data }">
|
||||
<h3>{{ solar2lunar(data.day).cDay }}</h3>
|
||||
<h5>
|
||||
{{ solar2lunar(data.day).IDayCn }}
|
||||
<span>
|
||||
{{ solar2lunar(data.day).gzDay }}
|
||||
{{ solar2lunar(data.day).astro }}
|
||||
</span>
|
||||
</h5>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dayjs from 'dayjs'
|
||||
import { solar2lunar } from '@/plugins/VabCalendar'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Calendar',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
calendar: '',
|
||||
})
|
||||
const selectDate = (val) => {
|
||||
state.calendar.selectDate(val)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dayjs,
|
||||
selectDate,
|
||||
solar2lunar,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.calendar-container {
|
||||
:deep() {
|
||||
.el-calendar {
|
||||
&-day {
|
||||
h5 {
|
||||
span {
|
||||
float: right;
|
||||
font-size: $base-font-size-small;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
216
front-end/src/views/vab/description/index.vue
Normal file
216
front-end/src/views/vab/description/index.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="description-container">
|
||||
<vab-card shadow="never">
|
||||
<!-- <el-radio-group v-model="size">
|
||||
<el-radio label="large">默认</el-radio>
|
||||
<el-radio label="default">中等</el-radio>
|
||||
<el-radio label="small">小</el-radio>
|
||||
</el-radio-group> -->
|
||||
|
||||
<el-descriptions
|
||||
border
|
||||
:column="3"
|
||||
:size="size"
|
||||
title="With border"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button size="small" type="primary">Operation</el-button>
|
||||
</template>
|
||||
<el-descriptions-item>
|
||||
<template #label>Username</template>
|
||||
kooriookami
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>Telephone</template>
|
||||
18100000000
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>Place</template>
|
||||
Suzhou
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>Remarks</template>
|
||||
<el-tag size="small">School</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>Address</template>
|
||||
No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu
|
||||
Province
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions :column="3" :size="size" title="Without border">
|
||||
<template #extra>
|
||||
<el-button size="small" type="primary">Operation</el-button>
|
||||
</template>
|
||||
<el-descriptions-item label="Username">
|
||||
kooriookami
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Telephone">
|
||||
18100000000
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Place">
|
||||
Suzhou
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Remarks">
|
||||
<el-tag size="small">School</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Address">
|
||||
No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu
|
||||
Province
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions
|
||||
border
|
||||
:column="4"
|
||||
direction="vertical"
|
||||
:size="size"
|
||||
title="Vertical list with border"
|
||||
>
|
||||
<el-descriptions-item label="Username">
|
||||
kooriookami
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Telephone">
|
||||
18100000000
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Place" :span="2">
|
||||
Suzhou
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Remarks">
|
||||
<el-tag size="small">School</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Address">
|
||||
No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu
|
||||
Province
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions
|
||||
:column="4"
|
||||
direction="vertical"
|
||||
:size="size"
|
||||
title="Vertical list without border"
|
||||
>
|
||||
<el-descriptions-item label="Username">
|
||||
kooriookami
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Telephone">
|
||||
18100000000
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Place" :span="2">
|
||||
Suzhou
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Remarks">
|
||||
<el-tag size="small">School</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Address">
|
||||
No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu
|
||||
Province
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
手风琴
|
||||
<el-switch
|
||||
v-model="accordion"
|
||||
inline-prompt
|
||||
@change="handleAccordion"
|
||||
/>
|
||||
|
||||
<el-collapse v-model="activeName" :accordion="accordion">
|
||||
<el-collapse-item name="1" title="Consistency">
|
||||
<div>
|
||||
Consistent with real life: in line with the process and
|
||||
logic of real life, and comply with languages and habits
|
||||
that the users are used to;
|
||||
</div>
|
||||
<div>
|
||||
Consistent within interface: all elements should be
|
||||
consistent, such as: design style, icons and texts,
|
||||
position of elements, etc.
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="2" title="Feedback">
|
||||
<div>
|
||||
Operation feedback: enable the users to clearly perceive
|
||||
their operations by style updates and interactive
|
||||
effects;
|
||||
</div>
|
||||
<div>
|
||||
Visual feedback: reflect current state by updating or
|
||||
rearranging elements of the page.
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="3" title="Efficiency">
|
||||
<div>
|
||||
Simplify the process: keep operating process simple and
|
||||
intuitive;
|
||||
</div>
|
||||
<div>
|
||||
Definite and clear: enunciate your intentions clearly so
|
||||
that the users can quickly understand and make
|
||||
decisions;
|
||||
</div>
|
||||
<div>
|
||||
Easy to identify: the interface should be
|
||||
straightforward, which helps the users to identify and
|
||||
frees them from memorizing and recalling.
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="4" title="Controllability">
|
||||
<div>
|
||||
Decision making: giving advices about operations is
|
||||
acceptable, but do not make decisions for the users;
|
||||
</div>
|
||||
<div>
|
||||
Controlled consequences: users should be granted the
|
||||
freedom to operate, including canceling, aborting or
|
||||
terminating current operation.
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Description',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
size: 'default',
|
||||
accordion: false,
|
||||
activeName: ['1', '2', '3', '4'],
|
||||
})
|
||||
|
||||
const handleAccordion = (val) => {
|
||||
if (val) state.activeName = '1'
|
||||
else state.activeName = ['1', '2', '3', '4']
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleAccordion,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.description-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-descriptions {
|
||||
padding-top: $base-padding !important;
|
||||
}
|
||||
|
||||
.el-collapse {
|
||||
margin-top: $base-margin !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
111
front-end/src/views/vab/editor/wangEditor.vue
Normal file
111
front-end/src/views/vab/editor/wangEditor.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="wang-editor-container">
|
||||
<toolbar :editor="editorRef" style="border-bottom: 1px solid #e8e8e8" />
|
||||
<editor
|
||||
v-model="html"
|
||||
class="wang-editor-content"
|
||||
:default-config="editorConfig"
|
||||
style="height: 300px"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
<div class="wang-editor-footer">
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
|
||||
import type { IDomEditor } from '@wangeditor/editor'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WangEditor',
|
||||
components: { Editor, Toolbar },
|
||||
setup() {
|
||||
const $baseMessage: any = inject('$baseMessage')
|
||||
const $baseAlert: any = inject('$baseAlert')
|
||||
const editorRef = shallowRef<IDomEditor | undefined>()
|
||||
const html = ref(
|
||||
'<h1>一级标题</h1><h2>二级标题</h2><h3>三级标题</h3><p>hello world ~~~ </p><blockquote>blockquote</blockquote><pre><code class="language-javascript">const a = 100;</code></pre><p><img src="https://gcore.jsdelivr.net/gh/zxwk1998/image/table/vab-image-1.jpg" alt="" data-href="" style=""/></p>'
|
||||
)
|
||||
const editorConfig = ref({
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
server: '', // 你的服务器地址,注意:当前接口格式特殊与其他vab接口不同,请查看vip文档
|
||||
fieldName: 'vab-file-name',
|
||||
allowedFileTypes: ['image/*'],
|
||||
headers: {}, // 如需传递token请写到在这里
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const handleCreated = (editor: IDomEditor) => {
|
||||
editorRef.value = editor
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
const onSubmit = () => {
|
||||
$baseAlert(html.value)
|
||||
$baseMessage(
|
||||
'模拟保存成功',
|
||||
'success',
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
editorRef,
|
||||
html,
|
||||
editorConfig,
|
||||
handleCreated,
|
||||
onSubmit,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.wang-editor-container {
|
||||
padding: 0 !important;
|
||||
margin: -19px -19px 19px;
|
||||
overflow: hidden !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
&.w-e-full-screen-container {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
[classname='w-e-toolbar-init'] {
|
||||
border-bottom: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.wang-editor-content {
|
||||
width: 70%;
|
||||
height: 500px !important;
|
||||
padding: 0 40px;
|
||||
margin: 20px auto;
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.wang-editor-footer {
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media (width <= 576px) {
|
||||
.wang-editor-title,
|
||||
.wang-editor-content,
|
||||
.wang-editor-footer {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
178
front-end/src/views/vab/form/button.vue
Normal file
178
front-end/src/views/vab/form/button.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="button-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>基础用法</span></template>
|
||||
<el-button>默认按钮</el-button>
|
||||
<el-button type="primary">主要按钮</el-button>
|
||||
<el-button type="success">成功按钮</el-button>
|
||||
<el-button type="info">信息按钮</el-button>
|
||||
<el-button type="warning">警告按钮</el-button>
|
||||
<el-button type="danger">危险按钮</el-button>
|
||||
<el-button plain>朴素按钮</el-button>
|
||||
<el-button plain type="primary">主要按钮</el-button>
|
||||
<el-button plain type="success">成功按钮</el-button>
|
||||
<el-button plain type="info">信息按钮</el-button>
|
||||
<el-button plain type="warning">警告按钮</el-button>
|
||||
<el-button plain type="danger">危险按钮</el-button>
|
||||
<el-button round>圆角按钮</el-button>
|
||||
<el-button round type="primary">主要按钮</el-button>
|
||||
<el-button round type="success">成功按钮</el-button>
|
||||
<el-button round type="info">信息按钮</el-button>
|
||||
<el-button round type="warning">警告按钮</el-button>
|
||||
<el-button round type="danger">危险按钮</el-button>
|
||||
<el-button circle :icon="Search" />
|
||||
<el-button circle :icon="Edit" type="primary" />
|
||||
<el-button circle :icon="Check" type="success" />
|
||||
<el-button circle :icon="Message" type="info" />
|
||||
<el-button circle :icon="Star" type="warning" />
|
||||
<el-button circle :icon="Delete" type="danger" />
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-button disabled>默认按钮</el-button>
|
||||
<el-button disabled type="primary">主要按钮</el-button>
|
||||
<el-button disabled type="success">成功按钮</el-button>
|
||||
<el-button disabled type="info">信息按钮</el-button>
|
||||
<el-button disabled type="warning">警告按钮</el-button>
|
||||
<el-button disabled type="danger">危险按钮</el-button>
|
||||
<el-button disabled plain>朴素按钮</el-button>
|
||||
<el-button disabled plain type="primary">主要按钮</el-button>
|
||||
<el-button disabled plain type="success">成功按钮</el-button>
|
||||
<el-button disabled plain type="info">信息按钮</el-button>
|
||||
<el-button disabled plain type="warning">警告按钮</el-button>
|
||||
<el-button disabled plain type="danger">危险按钮</el-button>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>文字按钮</span>
|
||||
</template>
|
||||
<el-button text type="primary">文字按钮</el-button>
|
||||
<el-button disabled text>文字按钮</el-button>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>element内置图标按钮</span>
|
||||
</template>
|
||||
<el-button :icon="Edit" type="primary" />
|
||||
<el-button :icon="Share" type="primary" />
|
||||
<el-button :icon="Delete" type="primary" />
|
||||
<el-button :icon="Search" type="primary">搜索</el-button>
|
||||
<el-button type="primary">
|
||||
上传
|
||||
<el-icon class="el-icon--right"><upload /></el-icon>
|
||||
</el-button>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>自定义图标按钮</span>
|
||||
</template>
|
||||
<el-button type="primary">
|
||||
<vab-icon icon="24-hours-line" />
|
||||
</el-button>
|
||||
<el-button type="primary">
|
||||
<vab-icon icon="4k-line" />
|
||||
</el-button>
|
||||
<el-button type="primary">
|
||||
<vab-icon icon="a-b" />
|
||||
</el-button>
|
||||
<el-button type="primary">
|
||||
<vab-icon icon="account-box-line" />
|
||||
用户名
|
||||
</el-button>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>按钮组</span>
|
||||
</template>
|
||||
<el-button-group style="margin-right: 10px">
|
||||
<el-button :icon="ArrowLeft" type="primary">上一页</el-button>
|
||||
<el-button type="primary">
|
||||
下一页
|
||||
<el-icon class="el-icon--right"><arrow-right /></el-icon>
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
<el-button-group>
|
||||
<el-button :icon="Edit" type="primary" />
|
||||
<el-button :icon="Share" type="primary" />
|
||||
<el-button :icon="Delete" type="primary" />
|
||||
</el-button-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>加载中</span>
|
||||
</template>
|
||||
<el-button :loading="true" type="primary">加载中</el-button>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>不同尺寸</span>
|
||||
</template>
|
||||
<el-button>默认按钮</el-button>
|
||||
<el-button size="small">小型按钮</el-button>
|
||||
<el-button round>默认按钮</el-button>
|
||||
<el-button round size="small">小型按钮</el-button>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
Check,
|
||||
Delete,
|
||||
Edit,
|
||||
Message,
|
||||
Search,
|
||||
Share,
|
||||
Star,
|
||||
Upload,
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Button',
|
||||
components: { ArrowRight, Upload },
|
||||
setup() {
|
||||
return {
|
||||
Search,
|
||||
Edit,
|
||||
Check,
|
||||
Message,
|
||||
Star,
|
||||
Delete,
|
||||
ArrowLeft,
|
||||
Share,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.button-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-card__body {
|
||||
padding-bottom: 10px;
|
||||
|
||||
.el-button {
|
||||
margin: 0 10px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button-group {
|
||||
.el-button {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
front-end/src/views/vab/form/checkbox.vue
Normal file
80
front-end/src/views/vab/form/checkbox.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="checkbox-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>基础用法</span></template>
|
||||
<el-checkbox v-model="checked">备选项</el-checkbox>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>禁用状态</span></template>
|
||||
<el-checkbox v-model="checked1" disabled>备选项1</el-checkbox>
|
||||
<el-checkbox v-model="checked2" disabled>备选项</el-checkbox>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>多选框组</span></template>
|
||||
<el-checkbox-group v-model="checkList">
|
||||
<el-checkbox label="复选框 A" value="复选框 A" />
|
||||
<el-checkbox label="复选框 B" value="复选框 B" />
|
||||
<el-checkbox label="复选框 C" value="复选框 C" />
|
||||
<el-checkbox disabled label="禁用" value="禁用" />
|
||||
<el-checkbox disabled label="选中且禁用" value="选中且禁用" />
|
||||
</el-checkbox-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>可选项目数量的限制</span></template>
|
||||
<el-checkbox-group v-model="checkedCities" :max="2" :min="1">
|
||||
<el-checkbox v-for="city in cities" :key="city" :label="city">
|
||||
{{ city }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>按钮样式</span></template>
|
||||
<el-checkbox-group v-model="checkboxGroup1">
|
||||
<el-checkbox-button
|
||||
v-for="city in cities"
|
||||
:key="city"
|
||||
:label="city"
|
||||
>
|
||||
{{ city }}
|
||||
</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header><span>带有边框</span></template>
|
||||
<el-checkbox v-model="checked3" border label="备选项1" />
|
||||
<el-checkbox v-model="checked4" border label="备选项2" />
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Checkbox',
|
||||
setup() {
|
||||
const cityOptions = ['上海', '北京', '广州', '深圳']
|
||||
|
||||
const state = reactive({
|
||||
checked: true,
|
||||
checked1: false,
|
||||
checked2: true,
|
||||
checkList: ['选中且禁用', '复选框 A'],
|
||||
checkedCities: ['上海', '北京'],
|
||||
cities: cityOptions,
|
||||
checkboxGroup1: ['上海'],
|
||||
checked3: true,
|
||||
checked4: false,
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.checkbox-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
109
front-end/src/views/vab/form/components/Step1.vue
Normal file
109
front-end/src/views/vab/form/components/Step1.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" label-width="100px" :model="form" :rules="rules">
|
||||
<el-form-item label="付款账户" prop="payAccount">
|
||||
<el-input v-model="form.payAccount" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收款账户" prop="gatheringAccount">
|
||||
<el-input v-model="form.gatheringAccount" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收款人姓名" prop="gatheringName">
|
||||
<el-input v-model="form.gatheringName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="转账金额" prop="price">
|
||||
<el-input v-model="form.price" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="pay-button-group">
|
||||
<el-button type="primary" @click="handleSubmit">下一步</el-button>
|
||||
</div>
|
||||
<el-alert :closable="false">
|
||||
<h3>转账到支付宝</h3>
|
||||
<p>
|
||||
生活好,支付宝。生活好,支付宝。生活好,支付宝。生活好,支付宝。
|
||||
</p>
|
||||
<h3>转账到微信</h3>
|
||||
<p>
|
||||
微不可挡,万众一信。微不可挡,万众一信。微不可挡,万众一信。微不可挡,万众一信。
|
||||
</p>
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Step1',
|
||||
emits: ['change-step'],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
form: {
|
||||
payAccount: '****************',
|
||||
gatheringAccount: '****************',
|
||||
gatheringName: '***',
|
||||
price: '100',
|
||||
},
|
||||
rules: {
|
||||
payAccount: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择付款账户',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
gatheringAccount: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入收款账户',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: '账户名应为邮箱格式',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
gatheringName: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入收款人姓名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
price: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入转账金额',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
pattern: /^(\d+)((?:\.\d+)?)$/,
|
||||
message: '请输入合法金额数字',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const handleSubmit = () => {
|
||||
state['formRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
emit('change-step', 1, state.form)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSubmit,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pay-button-group {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
91
front-end/src/views/vab/form/components/Step2.vue
Normal file
91
front-end/src/views/vab/form/components/Step2.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" label-width="100px" :model="form" :rules="rules">
|
||||
<el-form-item label="付款账户">
|
||||
{{ infoData.payAccount }}
|
||||
</el-form-item>
|
||||
<el-form-item label="收款账户">
|
||||
{{ infoData.gatheringAccount }}
|
||||
</el-form-item>
|
||||
<el-form-item label="收款人姓名">
|
||||
{{ infoData.gatheringName }}
|
||||
</el-form-item>
|
||||
<el-form-item label="转账金额">
|
||||
<strong>¥{{ infoData.price }}元</strong>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付密码" prop="password">
|
||||
<el-input v-model="form.password" type="password" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="pay-button-group">
|
||||
<el-button :loading="loading" type="primary" @click="handleSubmit">
|
||||
提交
|
||||
</el-button>
|
||||
<el-button @click="handlePrev">上一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Step2',
|
||||
props: {
|
||||
infoData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
emits: ['change-step'],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
form: {
|
||||
password: '123456',
|
||||
},
|
||||
rules: {
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入支付密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
|
||||
const handleSubmit = () => {
|
||||
state['formRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
state.loading = true
|
||||
setTimeout(() => {
|
||||
emit('change-step', 2)
|
||||
state.loading = false
|
||||
}, 2000)
|
||||
} else {
|
||||
state.loading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
const handlePrev = () => {
|
||||
emit('change-step', 0)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSubmit,
|
||||
handlePrev,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pay-button-group {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
114
front-end/src/views/vab/form/components/Step3.vue
Normal file
114
front-end/src/views/vab/form/components/Step3.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pay-top-content">
|
||||
<vab-icon class="pay-success" icon="checkbox-circle-line" />
|
||||
<p>支付成功</p>
|
||||
</div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
class="pay-bottom"
|
||||
label-width="100px"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-form-item label="付款账户">
|
||||
{{ infoData.payAccount }}
|
||||
</el-form-item>
|
||||
<el-form-item label="收款账户">
|
||||
{{ infoData.gatheringAccount }}
|
||||
</el-form-item>
|
||||
<el-form-item label="收款人姓名">
|
||||
{{ infoData.gatheringName }}
|
||||
</el-form-item>
|
||||
<el-form-item label="转账金额">
|
||||
<strong>¥{{ infoData.price }}元</strong>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="pay-button-group">
|
||||
<el-button type="primary" @click="handlePrev">再转一笔</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Step3',
|
||||
props: {
|
||||
infoData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
emits: ['change-step'],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
form: {
|
||||
password: '123456',
|
||||
},
|
||||
rules: {
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入支付密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
|
||||
const handleSubmit = () => {
|
||||
state['formRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
state.loading = true
|
||||
setTimeout(() => {
|
||||
emit('change-step', 2)
|
||||
state.loading = false
|
||||
}, 2000)
|
||||
} else {
|
||||
state.loading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
const handlePrev = () => {
|
||||
emit('change-step', 0)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSubmit,
|
||||
handlePrev,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pay-top-content {
|
||||
text-align: center;
|
||||
|
||||
.pay-success {
|
||||
display: block;
|
||||
margin: $base-margin auto 5px auto;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
}
|
||||
|
||||
.pay-bottom {
|
||||
padding: $base-padding;
|
||||
margin-top: $base-margin;
|
||||
background: #f5f7f8;
|
||||
border: 1px dashed $base-border-color;
|
||||
border-radius: $base-border-radius;
|
||||
}
|
||||
|
||||
.pay-button-group {
|
||||
display: block;
|
||||
margin: $base-margin auto;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
278
front-end/src/views/vab/form/comprehensiveForm.vue
Normal file
278
front-end/src/views/vab/form/comprehensiveForm.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<div class="comprehensive-form-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
:lg="{ span: 12, offset: 6 }"
|
||||
:md="{ span: 20, offset: 2 }"
|
||||
:sm="{ span: 20, offset: 2 }"
|
||||
:xl="{ span: 12, offset: 6 }"
|
||||
:xs="24"
|
||||
>
|
||||
<vab-query-form>
|
||||
<vab-query-form-left-panel>
|
||||
<el-radio-group v-model="labelPosition">
|
||||
<el-radio-button label="left" value="left">
|
||||
左对齐
|
||||
</el-radio-button>
|
||||
<el-radio-button label="right" value="right">
|
||||
右对齐
|
||||
</el-radio-button>
|
||||
<el-radio-button label="top" value="top">
|
||||
顶部对齐
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</vab-query-form-left-panel>
|
||||
</vab-query-form>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
class="demo-form"
|
||||
:label-position="labelPosition"
|
||||
label-width="100px"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动区域" prop="region">
|
||||
<el-select
|
||||
v-model="form.region"
|
||||
placeholder="请选择活动区域"
|
||||
>
|
||||
<el-option label="区域一" value="shanghai" />
|
||||
<el-option label="区域二" value="beijing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" prop="date">
|
||||
<el-date-picker
|
||||
v-model="form.date"
|
||||
placeholder="选择日期时间"
|
||||
type="datetime"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="即时配送" prop="delivery">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动性质" prop="type">
|
||||
<el-checkbox-group v-model="form.type">
|
||||
<el-checkbox
|
||||
label="美食/餐厅线上活动"
|
||||
name="type"
|
||||
value="美食/餐厅线上活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="地推活动"
|
||||
name="type"
|
||||
value="地推活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="线下主题活动"
|
||||
name="type"
|
||||
value="线下主题活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="单纯品牌曝光"
|
||||
name="type"
|
||||
value="单纯品牌曝光"
|
||||
/>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="特殊资源" prop="resource">
|
||||
<el-radio-group v-model="form.resource">
|
||||
<el-radio label="线上品牌商赞助" />
|
||||
<el-radio label="线下场地免费" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动形式" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="评星">
|
||||
<el-rate v-model="form.rate" show-text />
|
||||
</el-form-item>
|
||||
<el-form-item label="行政区划">
|
||||
<el-cascader
|
||||
v-model="form.area"
|
||||
clearable
|
||||
filterable
|
||||
:options="areaOptions"
|
||||
:props="{ label: 'name', value: 'code' }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="穿梭框">
|
||||
<el-transfer
|
||||
v-model="form.transfer"
|
||||
:data="data"
|
||||
:filter-method="filterMethod"
|
||||
filter-placeholder="请输入城市拼音"
|
||||
filterable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitForm('formRef')"
|
||||
>
|
||||
立即创建
|
||||
</el-button>
|
||||
<el-button @click="resetForm('formRef')">
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/area'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ComprehensiveForm',
|
||||
setup() {
|
||||
const generateData = () => {
|
||||
const data = []
|
||||
const cities = ['上海', '北京', '广州']
|
||||
const pinyin = ['shanghai', 'beijing', 'guangzhou']
|
||||
cities.forEach((city, index) => {
|
||||
data.push({
|
||||
label: city,
|
||||
key: index,
|
||||
pinyin: pinyin[index],
|
||||
})
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
labelPosition: 'right',
|
||||
form: {
|
||||
name: '',
|
||||
region: '',
|
||||
date: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
description: '',
|
||||
rate: 0,
|
||||
area: [],
|
||||
transfer: [],
|
||||
},
|
||||
areaOptions: [],
|
||||
rules: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入活动名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 3,
|
||||
max: 5,
|
||||
message: '长度在 3 到 5 个字符',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
region: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择活动区域',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
date: [
|
||||
{
|
||||
type: 'date',
|
||||
required: true,
|
||||
message: '请选择日期',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
type: [
|
||||
{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请至少选择一个活动性质',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
resource: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择活动资源',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
description: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写活动形式',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: generateData(),
|
||||
filterMethod(query, item) {
|
||||
return item.pinyin.includes(query)
|
||||
},
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
state.areaOptions = list
|
||||
}
|
||||
|
||||
const submitForm = (formName) => {
|
||||
state[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
alert('submit!')
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('error submit!!')
|
||||
}
|
||||
})
|
||||
}
|
||||
const resetForm = (formName) => {
|
||||
state[formName].resetFields()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
submitForm,
|
||||
resetForm,
|
||||
fetchData,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.comprehensive-form-container {
|
||||
.demo-form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.el-form-item__content {
|
||||
.el-rate {
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-transfer__buttons {
|
||||
padding: 10px 10px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
front-end/src/views/vab/form/datePicker.vue
Normal file
141
front-end/src/views/vab/form/datePicker.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="date-picker-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>选择日</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value1"
|
||||
placeholder="选择日期"
|
||||
type="date"
|
||||
/>
|
||||
<el-date-picker
|
||||
v-model="value2"
|
||||
:disabled-date="disabledDate"
|
||||
placeholder="选择日期"
|
||||
:shortcuts="shortcuts"
|
||||
type="date"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>其他日期单位</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value3"
|
||||
format="yyyy 第 WW 周"
|
||||
placeholder="选择周"
|
||||
type="week"
|
||||
/>
|
||||
<el-date-picker
|
||||
v-model="value4"
|
||||
placeholder="选择月"
|
||||
type="month"
|
||||
/>
|
||||
<el-date-picker v-model="value5" placeholder="选择年" type="year" />
|
||||
<el-date-picker
|
||||
v-model="value6"
|
||||
placeholder="选择一个或多个日期"
|
||||
type="dates"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>选择日期范围</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value7"
|
||||
end-placeholder="结束日期"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>选择月份范围</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value8"
|
||||
end-placeholder="结束月份"
|
||||
range-separator="至"
|
||||
start-placeholder="开始月份"
|
||||
type="monthrange"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'DatePicker',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
disabledDate(time) {
|
||||
return time.getTime() > Date.now()
|
||||
},
|
||||
shortcuts: [
|
||||
{
|
||||
text: '今天',
|
||||
value: new Date(),
|
||||
},
|
||||
{
|
||||
text: '昨天',
|
||||
value: () => {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24)
|
||||
return date
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '一周前',
|
||||
value: () => {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
|
||||
return date
|
||||
},
|
||||
},
|
||||
],
|
||||
value1: '',
|
||||
value2: '',
|
||||
value3: '',
|
||||
value4: '',
|
||||
value5: '',
|
||||
value6: '',
|
||||
value7: '',
|
||||
value8: '',
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.date-picker-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-range-separator {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width: 180px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
& + .el-input {
|
||||
margin-right: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
65
front-end/src/views/vab/form/dateTimePicker.vue
Normal file
65
front-end/src/views/vab/form/dateTimePicker.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="date-time-picker-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>日期和时间点</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value1"
|
||||
placeholder="选择日期时间"
|
||||
type="datetime"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>日期和时间范围</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value2"
|
||||
end-placeholder="结束日期"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
type="datetimerange"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>默认的起始与结束时刻</span>
|
||||
</template>
|
||||
<el-date-picker
|
||||
v-model="value3"
|
||||
:default-time="value2"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="datetimerange"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'DateTimePicker',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value1: '',
|
||||
value2: [
|
||||
new Date(2000, 10, 10, 10, 10),
|
||||
new Date(2000, 10, 11, 10, 10),
|
||||
],
|
||||
value3: '',
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.date-time-picker-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
162
front-end/src/views/vab/form/input.vue
Normal file
162
front-end/src/views/vab/form/input.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div class="input-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="input1"
|
||||
placeholder="请输入内容"
|
||||
width="“200px”"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="input2"
|
||||
:disabled="true"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>可清空</span>
|
||||
</template>
|
||||
<el-input v-model="input3" clearable placeholder="请输入内容" />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>密码框</span>
|
||||
</template>
|
||||
<el-input v-model="input4" placeholder="请输入内容" show-password />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>带 icon 的输入框</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="input5"
|
||||
placeholder="请输入内容"
|
||||
style="float: left"
|
||||
:suffix-icon="Search"
|
||||
/>
|
||||
<el-input
|
||||
v-model="input6"
|
||||
placeholder="请输入内容"
|
||||
:prefix-icon="Search"
|
||||
style="float: left"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>复合型输入框</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="input7"
|
||||
placeholder="请输入内容"
|
||||
style="width: 300px"
|
||||
>
|
||||
<template #prepend>Http://</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-model="input8"
|
||||
placeholder="请输入内容"
|
||||
style="width: 300px"
|
||||
>
|
||||
<template #append>.com</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-model="input9"
|
||||
placeholder="请输入内容"
|
||||
style="width: 350px"
|
||||
>
|
||||
<template #prepend>
|
||||
<el-select v-model="select" placeholder="请选择">
|
||||
<el-option label="选项1" :value="1" />
|
||||
<el-option label="选项2" :value="2" />
|
||||
<el-option label="选项3" :value="3" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button :icon="Search" />
|
||||
</template>
|
||||
</el-input>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>textarea</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="textarea"
|
||||
placeholder="请输入内容"
|
||||
:rows="2"
|
||||
type="textarea"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Input',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
input1: '',
|
||||
input2: '',
|
||||
input3: '',
|
||||
input4: '',
|
||||
input5: '',
|
||||
input6: '',
|
||||
input7: '',
|
||||
input8: '',
|
||||
input9: '',
|
||||
select: 1,
|
||||
textarea: '',
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-input {
|
||||
width: 180px;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
& + .el-input {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-textarea {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
.el-input {
|
||||
width: 90px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
75
front-end/src/views/vab/form/inputNumber.vue
Normal file
75
front-end/src/views/vab/form/inputNumber.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="input-number-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="num"
|
||||
label="描述文字"
|
||||
:max="10"
|
||||
:min="1"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-input-number v-model="num2" :disabled="true" />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>步数</span>
|
||||
</template>
|
||||
<el-input-number v-model="num3" :step="2" />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>精度</span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="num4"
|
||||
:max="10"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>按钮位置</span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="num5"
|
||||
controls-position="right"
|
||||
:max="10"
|
||||
:min="1"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'InputNumber',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
num: 1,
|
||||
num2: 1,
|
||||
num3: 5,
|
||||
num4: 1,
|
||||
num5: 1,
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.inputNumber-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
76
front-end/src/views/vab/form/link.vue
Normal file
76
front-end/src/views/vab/form/link.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="link-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-link href="https://element.eleme.io" target="_blank">
|
||||
默认链接
|
||||
</el-link>
|
||||
<el-link type="primary">主要链接</el-link>
|
||||
<el-link type="success">成功链接</el-link>
|
||||
<el-link type="warning">警告链接</el-link>
|
||||
<el-link type="danger">危险链接</el-link>
|
||||
<el-link type="info">信息链接</el-link>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-link disabled>默认链接</el-link>
|
||||
<el-link disabled type="primary">主要链接</el-link>
|
||||
<el-link disabled type="success">成功链接</el-link>
|
||||
<el-link disabled type="warning">警告链接</el-link>
|
||||
<el-link disabled type="danger">危险链接</el-link>
|
||||
<el-link disabled type="info">信息链接</el-link>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>下划线</span>
|
||||
</template>
|
||||
<el-link :underline="false">无下划线</el-link>
|
||||
<el-link>有下划线</el-link>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>图标</span>
|
||||
</template>
|
||||
<el-link :icon="Edit">编辑</el-link>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Edit } from '@element-plus/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Link',
|
||||
setup() {
|
||||
return {
|
||||
Edit,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.link-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-link {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
& + .el-link {
|
||||
margin-right: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
74
front-end/src/views/vab/form/radio.vue
Normal file
74
front-end/src/views/vab/form/radio.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="radio-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-radio v-model="radio" label="1">备选项</el-radio>
|
||||
<el-radio v-model="radio" label="2">备选项</el-radio>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-radio v-model="radio2" disabled label="禁用">备选项</el-radio>
|
||||
<el-radio v-model="radio2" disabled label="选中且禁用">
|
||||
备选项
|
||||
</el-radio>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>单选框组</span>
|
||||
</template>
|
||||
<el-radio-group v-model="radio3">
|
||||
<el-radio :label="3" value="3">备选项</el-radio>
|
||||
<el-radio :label="6" value="6">备选项</el-radio>
|
||||
<el-radio :label="9" value="9">备选项</el-radio>
|
||||
</el-radio-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>按钮样式</span>
|
||||
</template>
|
||||
<el-radio-group v-model="radio4">
|
||||
<el-radio-button label="上海" value="上海" />
|
||||
<el-radio-button label="北京" value="北京" />
|
||||
<el-radio-button label="广州" value="广州" />
|
||||
<el-radio-button label="深圳" value="深圳" />
|
||||
</el-radio-group>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>带有边框</span>
|
||||
</template>
|
||||
<el-radio v-model="radio5" border label="1">备选项1</el-radio>
|
||||
<el-radio v-model="radio5" border label="2">备选项2</el-radio>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Radio',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
radio: '1',
|
||||
radio2: '选中且禁用',
|
||||
radio3: 3,
|
||||
radio4: '上海',
|
||||
radio5: '1',
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.radio-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
52
front-end/src/views/vab/form/rate.vue
Normal file
52
front-end/src/views/vab/form/rate.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="rate-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-rate v-model="value1" />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>辅助文字</span>
|
||||
</template>
|
||||
<el-rate v-model="value2" show-text />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>只读</span>
|
||||
</template>
|
||||
<el-rate
|
||||
v-model="value3"
|
||||
disabled
|
||||
score-template="{value}"
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Rate',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value1: null,
|
||||
value2: null,
|
||||
value3: 3.7,
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rate-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
232
front-end/src/views/vab/form/select.vue
Normal file
232
front-end/src/views/vab/form/select.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<template>
|
||||
<div class="select-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>行政区划</span>
|
||||
<el-tag class="card-header-tag" type="danger">New</el-tag>
|
||||
</template>
|
||||
<el-cascader
|
||||
v-model="area"
|
||||
clearable
|
||||
filterable
|
||||
:options="areaOptions"
|
||||
:props="{ label: 'name', value: 'code' }"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>树选择</span>
|
||||
<el-tag class="card-header-tag" type="danger">New</el-tag>
|
||||
</template>
|
||||
<el-tree-select
|
||||
v-model="treeValue"
|
||||
:data="treeData"
|
||||
multiple
|
||||
show-checkbox
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-select v-model="value1" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options1"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>有禁用选项</span>
|
||||
</template>
|
||||
<el-select v-model="value2" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options2"
|
||||
:key="item.value"
|
||||
:disabled="item.disabled"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-select v-model="value1" disabled placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options1"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>可清空单选</span>
|
||||
</template>
|
||||
<el-select v-model="value1" clearable placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options1"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础多选</span>
|
||||
</template>
|
||||
<el-select v-model="value3" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options1"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>可搜索</span>
|
||||
</template>
|
||||
<el-select v-model="value1" filterable placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options1"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/area'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Select',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
options1: [
|
||||
{ value: '选项1', label: '黄金糕' },
|
||||
{ value: '选项2', label: '双皮奶' },
|
||||
{ value: '选项3', label: '蚵仔煎' },
|
||||
{ value: '选项4', label: '龙须面' },
|
||||
{ value: '选项5', label: '北京烤鸭' },
|
||||
],
|
||||
value1: '',
|
||||
options2: [
|
||||
{ value: '选项1', label: '黄金糕' },
|
||||
{ value: '选项2', label: '双皮奶', disabled: true },
|
||||
{ value: '选项3', label: '蚵仔煎' },
|
||||
{ value: '选项4', label: '龙须面' },
|
||||
{ value: '选项5', label: '北京烤鸭' },
|
||||
],
|
||||
value2: '',
|
||||
value3: [],
|
||||
area: [],
|
||||
treeValue: '',
|
||||
areaOptions: [],
|
||||
treeData: [
|
||||
{
|
||||
value: '1',
|
||||
label: 'Level one 1',
|
||||
children: [
|
||||
{
|
||||
value: '1-1',
|
||||
label: 'Level two 1-1',
|
||||
children: [
|
||||
{
|
||||
value: '1-1-1',
|
||||
label: 'Level three 1-1-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'Level one 2',
|
||||
children: [
|
||||
{
|
||||
value: '2-1',
|
||||
label: 'Level two 2-1',
|
||||
children: [
|
||||
{
|
||||
value: '2-1-1',
|
||||
label: 'Level three 2-1-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '2-2',
|
||||
label: 'Level two 2-2',
|
||||
children: [
|
||||
{
|
||||
value: '2-2-1',
|
||||
label: 'Level three 2-2-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: 'Level one 3',
|
||||
children: [
|
||||
{
|
||||
value: '3-1',
|
||||
label: 'Level two 3-1',
|
||||
children: [
|
||||
{
|
||||
value: '3-1-1',
|
||||
label: 'Level three 3-1-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '3-2',
|
||||
label: 'Level two 3-2',
|
||||
children: [
|
||||
{
|
||||
value: '3-2-1',
|
||||
label: 'Level three 3-2-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
state.areaOptions = list
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
81
front-end/src/views/vab/form/slider.vue
Normal file
81
front-end/src/views/vab/form/slider.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="slider-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<span class="demonstration">默认</span>
|
||||
<el-slider v-model="value1" />
|
||||
<span class="demonstration">自定义初始值</span>
|
||||
<el-slider v-model="value2" />
|
||||
<span class="demonstration">隐藏 Tooltip</span>
|
||||
<el-slider v-model="value3" :show-tooltip="false" />
|
||||
<span class="demonstration">格式化 Tooltip</span>
|
||||
<el-slider v-model="value4" :format-tooltip="formatTooltip" />
|
||||
<span class="demonstration">禁用</span>
|
||||
<el-slider v-model="value5" disabled />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>离散值</span>
|
||||
</template>
|
||||
<span class="demonstration">不显示间断点</span>
|
||||
<el-slider v-model="value6" :step="10" />
|
||||
<span class="demonstration">显示间断点</span>
|
||||
<el-slider v-model="value6" show-stops :step="10" />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>带有输入框</span>
|
||||
</template>
|
||||
<el-slider v-model="value7" show-input />
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>范围选择</span>
|
||||
</template>
|
||||
<el-slider v-model="value8" :max="10" range show-stops />
|
||||
</vab-card>
|
||||
<!-- <vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>竖向模式</span>
|
||||
</template>
|
||||
<el-slider v-model="value9" height="200px" vertical />
|
||||
</vab-card> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Slider',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value1: 0,
|
||||
value2: 50,
|
||||
value3: 36,
|
||||
value4: 48,
|
||||
value5: 42,
|
||||
value6: 0,
|
||||
value7: 0,
|
||||
value8: [4, 8],
|
||||
value9: 0,
|
||||
})
|
||||
|
||||
const formatTooltip = (val) => {
|
||||
return val / 100
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
formatTooltip,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.slider-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
114
front-end/src/views/vab/form/stepForm.vue
Normal file
114
front-end/src/views/vab/form/stepForm.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="step-form-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
:lg="{ span: 10, offset: 7 }"
|
||||
:md="{ span: 20, offset: 2 }"
|
||||
:sm="{ span: 20, offset: 2 }"
|
||||
:xl="{ span: 10, offset: 7 }"
|
||||
:xs="24"
|
||||
>
|
||||
<el-steps :active="active" align-center class="steps">
|
||||
<el-step description="填写转账信息" title="第1步" />
|
||||
<el-step description="确认转账信息" title="第2步" />
|
||||
<el-step description="完成" title="第3步" />
|
||||
</el-steps>
|
||||
<step1 v-if="active === 0" @change-step="handleSetStep" />
|
||||
<step2
|
||||
v-if="active === 1"
|
||||
:info-data="form"
|
||||
@change-step="handleSetStep"
|
||||
/>
|
||||
<step3
|
||||
v-if="active === 2"
|
||||
:info-data="form"
|
||||
@change-step="handleSetStep"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Step1 from './components/Step1'
|
||||
import Step2 from './components/Step2'
|
||||
import Step3 from './components/Step3'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'StepForm',
|
||||
components: { Step1, Step2, Step3 },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
active: 0,
|
||||
form: {},
|
||||
})
|
||||
|
||||
const handleSetStep = (active, form) => {
|
||||
state.active = active
|
||||
if (form) state.form = Object.assign(state.form, form)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSetStep,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.step-form-container {
|
||||
:deep() {
|
||||
.el-steps {
|
||||
margin: $base-margin auto $base-margin * 2 auto;
|
||||
|
||||
.el-step__title.is-process {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-step__description.is-process {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-step__head {
|
||||
&.is-process {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
|
||||
.el-step__icon.is-text {
|
||||
color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.el-step__line {
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-wait {
|
||||
.el-step__icon.is-text {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.el-step__line {
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-finish {
|
||||
.el-step__icon.is-text {
|
||||
color: var(--el-color-white);
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-step__line {
|
||||
height: 1px;
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
front-end/src/views/vab/form/switch.vue
Normal file
56
front-end/src/views/vab/form/switch.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="switch-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>基础用法</span>
|
||||
</template>
|
||||
<el-switch
|
||||
v-model="value"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>文字描述</span>
|
||||
</template>
|
||||
<el-switch
|
||||
v-model="value1"
|
||||
active-text="按月付费"
|
||||
inactive-text="按年付费"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>禁用状态</span>
|
||||
</template>
|
||||
<el-switch v-model="value2" disabled style="margin-right: 10px" />
|
||||
<el-switch v-model="value3" disabled />
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Switch',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value: true,
|
||||
value1: true,
|
||||
value2: true,
|
||||
value3: false,
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.switch-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
102
front-end/src/views/vab/form/timePicker.vue
Normal file
102
front-end/src/views/vab/form/timePicker.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="time-picker-container">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>固定时间点</span>
|
||||
</template>
|
||||
<el-time-select
|
||||
v-model="value"
|
||||
end="18:30"
|
||||
placeholder="选择时间"
|
||||
start="08:30"
|
||||
step="00:15"
|
||||
/>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>固定时间范围</span>
|
||||
</template>
|
||||
<el-time-select
|
||||
v-model="startTime"
|
||||
end="18:30"
|
||||
placeholder="开始时间"
|
||||
start="08:30"
|
||||
step="00:15"
|
||||
style="margin-right: 10px"
|
||||
/>
|
||||
<el-time-select
|
||||
v-model="endTime"
|
||||
end="18:30"
|
||||
:min-time="startTime"
|
||||
placeholder="结束时间"
|
||||
start="08:30"
|
||||
step="00:15"
|
||||
/>
|
||||
</vab-card>
|
||||
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>任意时间点</span>
|
||||
</template>
|
||||
<el-time-picker
|
||||
v-model="value1"
|
||||
:disabled-hours="disabledHours"
|
||||
:disabled-minutes="disabledMinutes"
|
||||
:disabled-seconds="disabledSeconds"
|
||||
placeholder="任意时间点"
|
||||
/>
|
||||
</vab-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Timepicker',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value: '',
|
||||
value1: new Date(2016, 9, 10, 18, 40),
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
})
|
||||
const makeRange = (start, end) => {
|
||||
const result = []
|
||||
for (let i = start; i <= end; i++) {
|
||||
result.push(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const disabledHours = () => {
|
||||
return makeRange(0, 16).concat(makeRange(19, 23))
|
||||
}
|
||||
const disabledMinutes = (hour) => {
|
||||
if (hour === 17) {
|
||||
return makeRange(0, 29)
|
||||
}
|
||||
if (hour === 18) {
|
||||
return makeRange(31, 59)
|
||||
}
|
||||
}
|
||||
const disabledSeconds = (hour, minute) => {
|
||||
if (hour === 18 && minute === 30) {
|
||||
return makeRange(1, 59)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
disabledHours,
|
||||
disabledMinutes,
|
||||
disabledSeconds,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.time-picker-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
55
front-end/src/views/vab/icon/customSvg.vue
Normal file
55
front-end/src/views/vab/icon/customSvg.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="custom-svg-container">
|
||||
<!-- 具体如何自定义查看文档,文档搜索vab-icon可看到用法 -->
|
||||
<el-row :gutter="15">
|
||||
<el-col :lg="2" :md="3" :sm="8" :xl="2" :xs="6">
|
||||
<vab-card shadow="never">
|
||||
<vab-icon icon="vab" is-custom-svg />
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="2" :md="3" :sm="8" :xl="2" :xs="6">
|
||||
<vab-card shadow="never">
|
||||
<vab-icon icon="vuejs-fill" is-custom-svg />
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
name: 'CustomSvg',
|
||||
setup() {
|
||||
return {}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use 'sass:math';
|
||||
|
||||
.custom-svg-container {
|
||||
:deep() {
|
||||
.el-card__body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60px;
|
||||
max-height: 60px;
|
||||
padding: #{math.div($base-padding, 1.4)};
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
font-size: 28px;
|
||||
color: var(--el-color-grey);
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
transition: $base-transition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
230
front-end/src/views/vab/icon/defaultIcon.vue
Normal file
230
front-end/src/views/vab/icon/defaultIcon.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div class="remix-icon-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form inline label-width="80px" @submit.prevent>
|
||||
<el-form-item label="图标名称">
|
||||
<el-input v-model="queryForm.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label-width="0">
|
||||
<el-button
|
||||
:icon="Search"
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="queryData"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
|
||||
<el-form-item label="文字大小">
|
||||
<el-input-number
|
||||
v-model="queryForm.num"
|
||||
:max="40"
|
||||
:min="12"
|
||||
style="width: 120px; margin-right: 10px"
|
||||
/>
|
||||
px
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="0">
|
||||
<el-checkbox
|
||||
v-model="queryForm.colorful"
|
||||
label="多彩图标"
|
||||
@change="queryData"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col v-if="emptyShow" :span="24">
|
||||
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||
</el-col>
|
||||
<el-col
|
||||
v-for="(item, index) in queryIcon"
|
||||
:key="index"
|
||||
:lg="2"
|
||||
:md="3"
|
||||
:sm="8"
|
||||
:xl="2"
|
||||
:xs="6"
|
||||
>
|
||||
<vab-card @click="handleCopyIcon(item.icon, $event)">
|
||||
<vab-icon
|
||||
:icon="item.icon"
|
||||
:style="{
|
||||
color: queryForm.colorful
|
||||
? item.color
|
||||
: 'var(--el-color-grey)',
|
||||
fontSize: queryForm.num + 'px',
|
||||
}"
|
||||
/>
|
||||
</vab-card>
|
||||
<div
|
||||
class="icon-text"
|
||||
@click="handleCopyText(item.icon, $event)"
|
||||
>
|
||||
{{ item.icon }}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="queryForm.pageNo"
|
||||
:layout="layout"
|
||||
:page-size="queryForm.pageSize"
|
||||
:page-sizes="[72, 144, 216, 288]"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { getIconList } from '@/api/defaultIcon'
|
||||
import clip from '@/utils/clipboard'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DefaultIcon',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
queryIcon: [],
|
||||
total: 0,
|
||||
queryForm: {
|
||||
pageNo: 1,
|
||||
pageSize: 108,
|
||||
title: '',
|
||||
colorful: false,
|
||||
num: 28,
|
||||
},
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
emptyShow: true,
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await getIconList(state.queryForm)
|
||||
state.queryIcon = list.map((icon) => {
|
||||
return { icon, color: randomHexColor() }
|
||||
})
|
||||
|
||||
state.total = total
|
||||
state.emptyShow = false
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
state.queryForm.pageSize = val
|
||||
fetchData()
|
||||
}
|
||||
const handleCurrentChange = (val) => {
|
||||
state.queryForm.pageNo = val
|
||||
fetchData()
|
||||
}
|
||||
const queryData = () => {
|
||||
state.queryForm.pageNo = 1
|
||||
fetchData()
|
||||
}
|
||||
const handleCopyText = (item, event) => {
|
||||
clip(item, event)
|
||||
}
|
||||
const handleCopyIcon = (item, event) => {
|
||||
clip(`<vab-icon icon="${item}" />`, event)
|
||||
}
|
||||
const randomHexColor = () => {
|
||||
return _.shuffle([
|
||||
'#1890FF',
|
||||
'#36CBCB',
|
||||
'#4ECB73',
|
||||
'#FBD437',
|
||||
'#F2637B',
|
||||
'#975FE5',
|
||||
])
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
queryData,
|
||||
handleCopyText,
|
||||
handleCopyIcon,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use 'sass:math';
|
||||
|
||||
.remix-icon-container {
|
||||
:deep() {
|
||||
.el-form--inline {
|
||||
.el-form-item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60px;
|
||||
max-height: 60px;
|
||||
padding: #{math.div($base-padding, 1.4)};
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
font-size: 28px;
|
||||
color: var(--el-color-grey);
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
transition: $base-transition;
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
font-size: $base-font-size-small;
|
||||
color: rgb(255 255 255);
|
||||
text-align: center;
|
||||
content: '点击复制';
|
||||
background-color: var(--el-color-primary);
|
||||
transition: $base-transition;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
i {
|
||||
margin-top: -#{math.div($base-margin, 1.2)};
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
height: 30px;
|
||||
margin-top: -15px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
front-end/src/views/vab/icon/iconSelector.vue
Normal file
56
front-end/src/views/vab/icon/iconSelector.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="icon-selector-container">
|
||||
<el-popover
|
||||
popper-class="icon-selector-popper"
|
||||
trigger="hover"
|
||||
:width="292"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button>
|
||||
<vab-icon :icon="icon" />
|
||||
图标选择器
|
||||
<vab-icon icon="arrow-down-s-line" />
|
||||
</el-button>
|
||||
</template>
|
||||
<vab-icon-selector @handle-icon="handleIcon" />
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabIconSelector from '@/plugins/VabIconSelector'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IconSelector',
|
||||
components: { VabIconSelector },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
icon: '24-hours-fill',
|
||||
})
|
||||
|
||||
const handleIcon = (item) => {
|
||||
state.icon = item
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleIcon,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.icon-selector-popper {
|
||||
.vab-card{
|
||||
&:hover{
|
||||
border-color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
i{
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
202
front-end/src/views/vab/list/index.vue
Normal file
202
front-end/src/views/vab/list/index.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="list-container">
|
||||
<el-row :gutter="20">
|
||||
<vab-query-form>
|
||||
<vab-query-form-top-panel :span="24">
|
||||
<el-form inline :model="queryForm" @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model.trim="queryForm.title"
|
||||
clearable
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:icon="Search"
|
||||
type="primary"
|
||||
@click="queryData"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-top-panel>
|
||||
</vab-query-form>
|
||||
<el-col v-if="emptyShow" :span="24">
|
||||
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<ul v-loading="listLoading">
|
||||
<li
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
class="list-item"
|
||||
>
|
||||
<div class="list-item-meta">
|
||||
<div class="list-item-meta-avatar">
|
||||
<el-image :src="item.img" />
|
||||
</div>
|
||||
<div class="list-item-meta-content">
|
||||
<div class="list-item-meta-title">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="list-item-meta-description">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item-meta-content">
|
||||
<div class="list-item-meta-item">
|
||||
<span>时间</span>
|
||||
<p>{{ item.datetime }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item-meta-content">
|
||||
<el-progress :percentage="item.percentage" />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="queryForm.pageNo"
|
||||
:layout="layout"
|
||||
:page-size="queryForm.pageSize"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'List',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
list: [],
|
||||
total: 0,
|
||||
queryForm: { pageNo: 1, pageSize: 10, title: '' },
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
listLoading: true,
|
||||
emptyShow: true,
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
state.listLoading = true
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await getList(state.queryForm)
|
||||
state.list = list
|
||||
state.total = total
|
||||
state.listLoading = false
|
||||
state.emptyShow = false
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
state.queryForm.pageSize = val
|
||||
fetchData()
|
||||
}
|
||||
const handleCurrentChange = (val) => {
|
||||
state.queryForm.pageNo = val
|
||||
fetchData()
|
||||
}
|
||||
const queryData = () => {
|
||||
state.queryForm.pageNo = 1
|
||||
fetchData()
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
queryData,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-container {
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
list-style: none;
|
||||
|
||||
.list-item {
|
||||
padding: $base-padding;
|
||||
border-bottom: 1px solid #{$base-border-color};
|
||||
|
||||
&-meta {
|
||||
display: flex;
|
||||
flex: 1 1;
|
||||
align-items: flex-start;
|
||||
|
||||
&-avatar {
|
||||
margin-right: 16px;
|
||||
|
||||
:deep() {
|
||||
.el-image {
|
||||
width: 61px;
|
||||
height: 61px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
flex: 1 0;
|
||||
width: 0;
|
||||
color: rgb(0 0 0 / 85%);
|
||||
}
|
||||
|
||||
&-title {
|
||||
margin-top: 11px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
color: rgb(0 0 0 / 85%);
|
||||
}
|
||||
|
||||
&-description {
|
||||
font-size: 14px;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: inline-block;
|
||||
height: 61px;
|
||||
margin-left: 40px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
|
||||
> span {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
> p {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.el-progress {
|
||||
margin-top: 21px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
390
front-end/src/views/vab/permission/index.vue
Normal file
390
front-end/src/views/vab/permission/index.vue
Normal file
@@ -0,0 +1,390 @@
|
||||
<template>
|
||||
<div class="roles-container">
|
||||
<el-alert
|
||||
v-if="!loginInterception"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="检测到您当前的登录拦截已关闭,无法模拟切换角色功能,请在src/config/setting.config.js中配置loginInterception为ture,开启登录拦截"
|
||||
type="success"
|
||||
/>
|
||||
<el-alert
|
||||
:closable="false"
|
||||
show-icon
|
||||
:title="`当前路由模式为:{ authentication:${authentication} },是否开启角色权限控制功能:{ rolesControl:${rolesControl} }`"
|
||||
type="success"
|
||||
/>
|
||||
|
||||
<el-form label-position="top" label-width="140px" :model="form">
|
||||
<el-form-item label="账号切换">
|
||||
<el-radio-group
|
||||
v-model="form.account"
|
||||
@change="handleChangeRole"
|
||||
>
|
||||
<el-radio-button label="admin" value="admin">
|
||||
admin
|
||||
</el-radio-button>
|
||||
<el-radio-button label="editor" value="editor">
|
||||
editor
|
||||
</el-radio-button>
|
||||
<el-radio-button label="test" value="test">
|
||||
test
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="过期Token模拟访问(令牌失效5s)">
|
||||
<el-button type="primary" @click="handleRefreshToken">
|
||||
点击模拟token过期访问接口,无痛刷新
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前账号">
|
||||
<el-descriptions border :column="3" direction="vertical">
|
||||
<el-descriptions-item>
|
||||
<template #label>角色</template>
|
||||
<el-tag>{{ JSON.stringify(role) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>权限点</template>
|
||||
<el-tag>{{ JSON.stringify(permission) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>token</template>
|
||||
<el-tag>{{ token }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-form-item>
|
||||
<el-form-item label="按钮级角色">
|
||||
<el-button v-permissions="['Admin']" type="primary">
|
||||
拥有["Admin"]角色的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{ role: ['Admin'], mode: 'except' }"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["Admin"]角色的按钮
|
||||
</el-button>
|
||||
<el-button v-permissions="['Editor']" type="primary">
|
||||
拥有["Editor"]角色的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{ role: ['Editor'], mode: 'except' }"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["Editor"]角色的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{ role: ['Admin', 'Editor'], mode: 'allOf' }"
|
||||
type="primary"
|
||||
>
|
||||
同时拥有["Admin","Editor"]角色的按钮
|
||||
</el-button>
|
||||
<el-button v-permissions="['Test']" type="primary">
|
||||
拥有["Test"]角色的按钮
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<!-- 注意其中roles-代表组件name,这样可以区分到具体页面 -->
|
||||
<el-form-item label="按钮级权限点">
|
||||
<el-button
|
||||
v-permissions="{ permission: ['read:system'] }"
|
||||
type="primary"
|
||||
>
|
||||
拥有["read:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{
|
||||
permission: ['read:system'],
|
||||
mode: 'except',
|
||||
}"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["'read:system'"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{ permission: ['write:system'] }"
|
||||
type="primary"
|
||||
>
|
||||
拥有["write:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{
|
||||
permission: ['write:system'],
|
||||
mode: 'except',
|
||||
}"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["write:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{ permission: ['delete:system'] }"
|
||||
type="primary"
|
||||
>
|
||||
拥有["delete:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{
|
||||
permission: ['delete:system'],
|
||||
mode: 'except',
|
||||
}"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["delete:system"]权限点的按钮
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="按钮级角色&权限点">
|
||||
<el-button
|
||||
v-permissions="{
|
||||
role: ['Admin'],
|
||||
permission: ['delete:system'],
|
||||
}"
|
||||
type="primary"
|
||||
>
|
||||
拥有["Admin"]角色或者["delete:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{
|
||||
role: ['Editor'],
|
||||
permission: ['read:system'],
|
||||
mode: 'allOf',
|
||||
}"
|
||||
type="primary"
|
||||
>
|
||||
拥有["Editor"]角色和["read:system"]权限点的按钮
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permissions="{
|
||||
role: ['Admin'],
|
||||
permission: ['delete:system'],
|
||||
mode: 'except',
|
||||
}"
|
||||
type="danger"
|
||||
>
|
||||
未拥有["Admin"]和["delete:system"]权限点的按钮
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="路由" />
|
||||
</el-form>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-table
|
||||
border
|
||||
:data="tableData"
|
||||
default-expand-all
|
||||
row-key="path"
|
||||
:tree-props="{
|
||||
children: 'children',
|
||||
hasChildren: 'hasChildren',
|
||||
}"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="name"
|
||||
prop="name"
|
||||
show-overflow-tooltip
|
||||
width="220"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="path"
|
||||
prop="path"
|
||||
show-overflow-tooltip
|
||||
width="220"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="component"
|
||||
prop="component"
|
||||
show-overflow-tooltip
|
||||
width="220"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="redirect"
|
||||
prop="redirect"
|
||||
show-overflow-tooltip
|
||||
width="220"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="title"
|
||||
prop="meta.title"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="icon"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
<vab-icon
|
||||
v-if="row.meta.icon"
|
||||
:icon="row.meta.icon"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="noClosable"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
{{ row.meta.noClosable || false }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="noKeepAlive"
|
||||
show-overflow-tooltip
|
||||
width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
<template v-if="!row.meta.noKeepAlive">
|
||||
false
|
||||
</template>
|
||||
<template v-else>true</template>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="badge"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
{{ row.meta.badge }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="isCustomSvg"
|
||||
show-overflow-tooltip
|
||||
width="140"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
{{ row.meta.isCustomSvg || false }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="tabHidden"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.meta">
|
||||
<template v-if="!row.meta.tabHidden">
|
||||
false
|
||||
</template>
|
||||
<template v-else>true</template>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Random } from 'mockjs'
|
||||
import { useAclStore } from '@/store/modules/acl'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import {
|
||||
authentication,
|
||||
loginInterception,
|
||||
rolesControl,
|
||||
tokenTableName,
|
||||
} from '@/config'
|
||||
import { getList } from '@/api/router'
|
||||
import { filterRoutes } from '@/utils/routes'
|
||||
import { expireToken } from '@/api/refreshToken'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Permission',
|
||||
setup() {
|
||||
const $baseLoading = inject('$baseLoading')
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const aclStore = useAclStore()
|
||||
const { role, permission } = storeToRefs(aclStore)
|
||||
const userStore = useUserStore()
|
||||
const { username, token } = storeToRefs(userStore)
|
||||
|
||||
const state = reactive({
|
||||
form: {
|
||||
account: username.value,
|
||||
},
|
||||
tableData: [],
|
||||
res: [],
|
||||
authentication,
|
||||
loginInterception,
|
||||
rolesControl,
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
state.tableData = filterRoutes([...list])
|
||||
}
|
||||
const handleChangeRole = async () => {
|
||||
$baseLoading(0, '正在切换账号请稍后...')
|
||||
await localStorage.setItem(
|
||||
tokenTableName,
|
||||
`${state.form.account}-token-${Random.guid()}-${Date.now()}`
|
||||
)
|
||||
await location.reload()
|
||||
}
|
||||
const handleRefreshToken = async () => {
|
||||
const { msg } = await expireToken()
|
||||
$baseMessage(
|
||||
`${msg}: [${token.value}] `,
|
||||
'success',
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
}
|
||||
|
||||
fetchData()
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
role,
|
||||
permission,
|
||||
username,
|
||||
token,
|
||||
fetchData,
|
||||
handleChangeRole,
|
||||
handleRefreshToken,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.el-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-right: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
.el-form-item__content {
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
95
front-end/src/views/vab/table/components/TableEdit.vue
Normal file
95
front-end/src/views/vab/table/components/TableEdit.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogFormVisible"
|
||||
align-center
|
||||
:title="title"
|
||||
width="500px"
|
||||
@close="close"
|
||||
>
|
||||
<el-form ref="formRef" label-width="80px" :model="form" :rules="rules">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model.trim="form.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="作者" prop="author">
|
||||
<el-input v-model.trim="form.author" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="close">取 消</el-button>
|
||||
<el-button type="primary" @click="save">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { doEdit } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TableEdit',
|
||||
emits: ['fetch-data'],
|
||||
setup(props, { emit }) {
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
form: {
|
||||
title: '',
|
||||
author: '',
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: '请输入标题',
|
||||
},
|
||||
],
|
||||
author: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: '请输入作者',
|
||||
},
|
||||
],
|
||||
},
|
||||
title: '',
|
||||
dialogFormVisible: false,
|
||||
})
|
||||
|
||||
const showEdit = (row) => {
|
||||
if (!row) {
|
||||
state.title = '添加'
|
||||
} else {
|
||||
state.title = '编辑'
|
||||
state.form = JSON.parse(JSON.stringify(row))
|
||||
}
|
||||
state.dialogFormVisible = true
|
||||
}
|
||||
const close = () => {
|
||||
state['formRef'].resetFields()
|
||||
state.form = {
|
||||
title: '',
|
||||
author: '',
|
||||
}
|
||||
state.dialogFormVisible = false
|
||||
}
|
||||
const save = () => {
|
||||
state['formRef'].validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { msg } = await doEdit(state.form)
|
||||
$baseMessage(msg, 'success', 'vab-hey-message-success')
|
||||
emit('fetch-data')
|
||||
close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
showEdit,
|
||||
close,
|
||||
save,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
476
front-end/src/views/vab/table/comprehensiveTable.vue
Normal file
476
front-end/src/views/vab/table/comprehensiveTable.vue
Normal file
@@ -0,0 +1,476 @@
|
||||
<template>
|
||||
<div class="comprehensive-table-container auto-height-container">
|
||||
<vab-query-form>
|
||||
<vab-query-form-top-panel>
|
||||
<el-form
|
||||
inline
|
||||
label-width="49px"
|
||||
:model="queryForm"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="标题">
|
||||
<el-input
|
||||
v-model="queryForm.title"
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="作者">
|
||||
<el-input
|
||||
v-model="queryForm.author"
|
||||
placeholder="请输入作者"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="!fold" label="时间">
|
||||
<el-date-picker
|
||||
v-model="queryForm.datetime"
|
||||
placeholder="请选择日期"
|
||||
style="width: 192px"
|
||||
type="date"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:icon="Search"
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="queryData"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button text type="primary" @click="handleFold">
|
||||
<span v-if="fold">展开</span>
|
||||
<span v-else>合并</span>
|
||||
<vab-icon
|
||||
class="vab-dropdown"
|
||||
:class="{ 'vab-dropdown-active': fold }"
|
||||
icon="arrow-up-s-line"
|
||||
/>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-top-panel>
|
||||
<vab-query-form-left-panel :span="24">
|
||||
<el-button :icon="Plus" type="primary" @click="handleAdd">
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="Delete"
|
||||
type="danger"
|
||||
@click="handleDelete($event)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<!-- <el-button type="primary" @click="handleMessage">
|
||||
$baseMessage
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleAlert">
|
||||
$baseAlert
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">
|
||||
$baseConfirm
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleNotify">
|
||||
$baseNotify
|
||||
</el-button> -->
|
||||
<el-button type="primary" @click="handleDetailStayTable">
|
||||
停留在本页后台打开详情页后(不常用)
|
||||
</el-button>
|
||||
<el-badge class="item" value="New">
|
||||
<el-button
|
||||
style="margin: 0 0 10px !important"
|
||||
type="primary"
|
||||
@click="handleDetail"
|
||||
>
|
||||
详情页支持tab多开并高亮左侧菜单
|
||||
</el-button>
|
||||
</el-badge>
|
||||
</vab-query-form-left-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-table
|
||||
ref="tableSortRef"
|
||||
v-loading="listLoading"
|
||||
border
|
||||
:data="list"
|
||||
@selection-change="setSelectRows"
|
||||
@sort-change="tableSortChange"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
type="selection"
|
||||
width="55"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="序号"
|
||||
show-overflow-tooltip
|
||||
width="55"
|
||||
>
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="标题"
|
||||
prop="title"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="作者"
|
||||
prop="author"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column align="center" label="评级">
|
||||
<template #default="{ row }">
|
||||
<el-rate v-model="row.rate" disabled />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="头像">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
:preview-src-list="imageList"
|
||||
preview-teleported
|
||||
:src="row.img"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="点击量"
|
||||
prop="pageViews"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="开关"
|
||||
prop="switch"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tooltip
|
||||
:content="row.switch === 0 ? '点击开启' : '点击关闭'"
|
||||
:enterable="false"
|
||||
placement="top"
|
||||
>
|
||||
<el-switch v-model="row.switch" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="状态" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-tooltip
|
||||
class="item"
|
||||
:content="row.status"
|
||||
effect="dark"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-tag :type="statusFilter(row.status)">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="时间"
|
||||
prop="datetime"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
fixed="right"
|
||||
label="操作"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="handleDetail(row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button text type="primary" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button text type="primary" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||
</template>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="queryForm.pageNo"
|
||||
:layout="layout"
|
||||
:page-size="queryForm.pageSize"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
<edit ref="editRef" @fetch-data="fetchData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Delete, Plus, Search } from '@element-plus/icons-vue'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
import { useRoutesStore } from '@/store/modules/routes'
|
||||
import { doDelete, getList } from '@/api/table'
|
||||
import { handleMatched, handleTabs } from '@/utils/routes'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ComprehensiveTable',
|
||||
components: {
|
||||
Edit: defineAsyncComponent(() => import('./components/TableEdit')),
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
|
||||
const $baseConfirm = inject('$baseConfirm')
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
const $baseAlert = inject('$baseAlert')
|
||||
const $baseNotify = inject('$baseNotify')
|
||||
|
||||
const routesStore = useRoutesStore()
|
||||
const { getRoutes: routes } = storeToRefs(routesStore)
|
||||
const tabsStore = useTabsStore()
|
||||
const { changeTabsMeta, addVisitedRoute } = tabsStore
|
||||
|
||||
const state = reactive({
|
||||
editRef: null,
|
||||
tableSortRef: null,
|
||||
fold: false,
|
||||
list: [],
|
||||
imageList: [],
|
||||
listLoading: true,
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
total: 0,
|
||||
selectRows: '',
|
||||
queryForm: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
title: '',
|
||||
author: '',
|
||||
datetime: '',
|
||||
},
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
state['tableSortRef'].doLayout()
|
||||
fetchData()
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
state.listLoading = true
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await getList(state.queryForm)
|
||||
state.list = list
|
||||
const imageList = []
|
||||
list.forEach((item) => {
|
||||
imageList.push(item.img)
|
||||
})
|
||||
state.imageList = imageList
|
||||
state.total = total
|
||||
state.listLoading = false
|
||||
|
||||
setTimeout(() => {
|
||||
toggleSelection([state.list[0]])
|
||||
}, 0)
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
state.queryForm.pageSize = val
|
||||
fetchData()
|
||||
}
|
||||
const handleCurrentChange = (val) => {
|
||||
state.queryForm.pageNo = val
|
||||
fetchData()
|
||||
}
|
||||
const queryData = () => {
|
||||
state.queryForm.pageNo = 1
|
||||
fetchData()
|
||||
}
|
||||
const statusFilter = (status) => {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'primary',
|
||||
deleted: 'danger',
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
const handleFold = () => {
|
||||
state.fold = !state.fold
|
||||
}
|
||||
const tableSortChange = () => {
|
||||
const imageList = []
|
||||
state.list.forEach((item) => {
|
||||
imageList.push(item.img)
|
||||
})
|
||||
state.imageList = imageList
|
||||
}
|
||||
const setSelectRows = (val) => {
|
||||
state.selectRows = val
|
||||
}
|
||||
const handleAdd = () => {
|
||||
state['editRef'].showEdit()
|
||||
}
|
||||
const handleEdit = (row) => {
|
||||
state['editRef'].showEdit(row)
|
||||
}
|
||||
const handleDelete = (row) => {
|
||||
if (row.id) {
|
||||
$baseConfirm('你确定要删除当前项吗', null, async () => {
|
||||
const { msg } = await doDelete({ ids: row.id })
|
||||
$baseMessage(msg, 'success', 'vab-hey-message-success')
|
||||
await fetchData()
|
||||
})
|
||||
} else {
|
||||
if (state.selectRows.length > 0) {
|
||||
const ids = state.selectRows
|
||||
.map((item) => item.id)
|
||||
.join(',')
|
||||
$baseConfirm('你确定要删除选中项吗', null, async () => {
|
||||
const { msg } = await doDelete({ ids })
|
||||
$baseMessage(
|
||||
msg,
|
||||
'success',
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
await fetchData()
|
||||
})
|
||||
} else {
|
||||
$baseMessage(
|
||||
'未选中任何行',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleDetailStayTable = async () => {
|
||||
for (let i = 0; i < state.selectRows.length; i++) {
|
||||
const matched = handleMatched(
|
||||
routes.value,
|
||||
'/vab/table/detail'
|
||||
)
|
||||
const tab = handleTabs({
|
||||
...matched[matched.length - 1],
|
||||
query: state.selectRows[i],
|
||||
})
|
||||
if (tab) {
|
||||
await addVisitedRoute(tab)
|
||||
await changeTabsMeta({
|
||||
title: '详情页',
|
||||
meta: {
|
||||
title: `${tab.query.title} 详情页`,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleDetail = (row) => {
|
||||
if (row.id)
|
||||
router.push({
|
||||
path: '/vab/table/detail',
|
||||
query: {
|
||||
...row,
|
||||
timestamp: Date.now(), //允许同一个详情页同时打开多次,否则会触发路由被缓存下次无法刷新的bug
|
||||
},
|
||||
})
|
||||
else {
|
||||
if (state.selectRows.length === 1) {
|
||||
router.push({
|
||||
path: '/vab/table/detail',
|
||||
query: {
|
||||
...state.selectRows[0],
|
||||
timestamp: Date.now(), //允许同一个详情页同时打开多次,否则会触发路由被缓存下次无法刷新的bug
|
||||
},
|
||||
})
|
||||
} else {
|
||||
$baseMessage(
|
||||
'请选择一行进行详情页跳转',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleMessage = () => {
|
||||
$baseMessage(
|
||||
'test1',
|
||||
'success',
|
||||
false,
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
}
|
||||
const handleAlert = () => {
|
||||
$baseAlert('11')
|
||||
// $baseAlert('11', '自定义标题', () => {
|
||||
// /* 可以写回调; */
|
||||
// })
|
||||
// $baseAlert('11', null, () => {
|
||||
// /* 可以写回调; */
|
||||
// })
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
$baseConfirm(
|
||||
'你确定要执行该操作?',
|
||||
null,
|
||||
() => {
|
||||
/* 可以写回调; */
|
||||
},
|
||||
() => {
|
||||
/* 可以写回调; */
|
||||
}
|
||||
)
|
||||
}
|
||||
const handleNotify = () => {
|
||||
$baseNotify('测试消息提示', 'test', 'success', 'bottom-right')
|
||||
}
|
||||
const toggleSelection = (rows) => {
|
||||
if (rows) {
|
||||
rows.forEach((row) => {
|
||||
state['tableSortRef'].toggleRowSelection(row)
|
||||
})
|
||||
} else {
|
||||
state['tableSortRef'].clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
queryData,
|
||||
statusFilter,
|
||||
handleFold,
|
||||
tableSortChange,
|
||||
setSelectRows,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleDetailStayTable,
|
||||
handleDetail,
|
||||
handleMessage,
|
||||
handleAlert,
|
||||
handleConfirm,
|
||||
handleNotify,
|
||||
fetchData,
|
||||
Delete,
|
||||
Plus,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
414
front-end/src/views/vab/table/customTable.vue
Normal file
414
front-end/src/views/vab/table/customTable.vue
Normal file
@@ -0,0 +1,414 @@
|
||||
<template>
|
||||
<div
|
||||
class="custom-table-container auto-height-container"
|
||||
:class="{ 'vab-fullscreen': isFullscreen }"
|
||||
>
|
||||
<vab-query-form>
|
||||
<vab-query-form-left-panel>
|
||||
<el-form
|
||||
inline
|
||||
label-width="0"
|
||||
:model="queryForm"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="queryForm.title"
|
||||
placeholder="标题"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:icon="Search"
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="queryData"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="Plus"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="Delete"
|
||||
type="danger"
|
||||
@click="handleDelete($event)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-left-panel>
|
||||
<vab-query-form-right-panel>
|
||||
<div class="stripe-panel">
|
||||
<el-checkbox v-model="stripe">斑马纹</el-checkbox>
|
||||
</div>
|
||||
<div class="border-panel">
|
||||
<el-checkbox v-model="border">边框(可拉伸列)</el-checkbox>
|
||||
</div>
|
||||
<el-button
|
||||
style="margin: 0 10px 10px 0 !important"
|
||||
text
|
||||
type="primary"
|
||||
@click="clickFullScreen"
|
||||
>
|
||||
<vab-icon
|
||||
:icon="
|
||||
isFullscreen
|
||||
? 'fullscreen-exit-fill'
|
||||
: 'fullscreen-fill'
|
||||
"
|
||||
/>
|
||||
</el-button>
|
||||
<el-popover popper-class="custom-table-radio" trigger="hover">
|
||||
<el-radio-group v-model="lineHeight">
|
||||
<el-radio label="large" value="large">大</el-radio>
|
||||
<el-radio label="default" value="default">中</el-radio>
|
||||
<el-radio label="small" value="small">小</el-radio>
|
||||
</el-radio-group>
|
||||
<template #reference>
|
||||
<el-button
|
||||
style="margin: 0 10px 10px 0 !important"
|
||||
text
|
||||
type="primary"
|
||||
>
|
||||
<vab-icon icon="line-height" />
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
popper-class="custom-table-checkbox"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
style="margin: 0 0 10px !important"
|
||||
text
|
||||
type="primary"
|
||||
>
|
||||
<vab-icon icon="settings-line" />
|
||||
</el-button>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList">
|
||||
<vab-draggable
|
||||
v-bind="dragOptions"
|
||||
item-key="{ element }"
|
||||
:list="columns"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div>
|
||||
<vab-icon icon="drag-drop-line" />
|
||||
<el-checkbox
|
||||
:disabled="
|
||||
element.disableCheck === true
|
||||
"
|
||||
:label="element.label"
|
||||
:value="element.label"
|
||||
>
|
||||
{{ element.label }}
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</template>
|
||||
</vab-draggable>
|
||||
</el-checkbox-group>
|
||||
</el-popover>
|
||||
</vab-query-form-right-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-table
|
||||
ref="tableSortRef"
|
||||
v-loading="listLoading"
|
||||
:border="border"
|
||||
:data="list"
|
||||
:size="lineHeight"
|
||||
:stripe="stripe"
|
||||
@selection-change="setSelectRows"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="55" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="序号"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- TODO element-plus未知原因不支持拖拽后宽度重新计算,暂时放弃 -->
|
||||
<el-table-column
|
||||
v-for="(item, index) in finallyColumns"
|
||||
:key="index"
|
||||
align="center"
|
||||
:label="item.label"
|
||||
:prop="item.prop"
|
||||
:sortable="item.sortable"
|
||||
width="auto"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="item.label === '评级'">
|
||||
<el-rate v-model="row.rate" disabled />
|
||||
</span>
|
||||
<span v-else>{{ row[item.prop] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button text type="primary" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<template #empty>
|
||||
<!-- <el-image
|
||||
class="vab-data-empty"
|
||||
:src="require('@/assets/empty_images/data_empty.png')"
|
||||
/> -->
|
||||
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||
</template>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="queryForm.pageNo"
|
||||
:layout="layout"
|
||||
:page-size="queryForm.pageSize"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
<edit ref="editRef" @fetch-data="fetchData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabDraggable from 'vuedraggable'
|
||||
import { Delete, Plus, Search, Setting } from '@element-plus/icons-vue'
|
||||
import { doDelete, getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CustomTable',
|
||||
components: {
|
||||
Edit: defineAsyncComponent(() => import('./components/TableEdit')),
|
||||
VabDraggable,
|
||||
},
|
||||
setup() {
|
||||
const $baseConfirm = inject('$baseConfirm')
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
tableSortRef: null,
|
||||
editRef: null,
|
||||
border: true,
|
||||
stripe: false,
|
||||
lineHeight: 'small',
|
||||
isFullscreen: false,
|
||||
checkList: ['标题', '作者', '评级', '点击量', '时间'],
|
||||
columns: [
|
||||
{
|
||||
label: '标题',
|
||||
prop: 'title',
|
||||
sortable: true,
|
||||
disableCheck: true,
|
||||
},
|
||||
{
|
||||
label: '作者',
|
||||
prop: 'author',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: '评级',
|
||||
prop: 'rate',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: '点击量',
|
||||
prop: 'pageViews',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: '时间',
|
||||
prop: 'datetime',
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
list: [],
|
||||
imageList: [],
|
||||
listLoading: true,
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
total: 0,
|
||||
selectRows: '',
|
||||
queryForm: {
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
title: '',
|
||||
},
|
||||
})
|
||||
|
||||
const dragOptions = computed(() => {
|
||||
return {
|
||||
animation: 600,
|
||||
group: 'description',
|
||||
}
|
||||
})
|
||||
const finallyColumns = computed(() => {
|
||||
return state.columns.filter((item) =>
|
||||
state.checkList.includes(item.label)
|
||||
)
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
state.listLoading = true
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await getList(state.queryForm)
|
||||
state.list = list
|
||||
const imageList = []
|
||||
list.forEach((item) => {
|
||||
imageList.push(item.img)
|
||||
})
|
||||
state.total = total
|
||||
state.listLoading = false
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
state.queryForm.pageSize = val
|
||||
fetchData()
|
||||
}
|
||||
const handleCurrentChange = (val) => {
|
||||
state.queryForm.pageNo = val
|
||||
fetchData()
|
||||
}
|
||||
const queryData = () => {
|
||||
state.queryForm.pageNo = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const clickFullScreen = () => {
|
||||
state.isFullscreen = !state.isFullscreen
|
||||
}
|
||||
|
||||
const setSelectRows = (val) => {
|
||||
state.selectRows = val
|
||||
}
|
||||
const handleAdd = () => {
|
||||
state['editRef'].showEdit()
|
||||
}
|
||||
const handleEdit = (row) => {
|
||||
state['editRef'].showEdit(row)
|
||||
}
|
||||
const handleDelete = (row) => {
|
||||
if (row.id) {
|
||||
$baseConfirm('你确定要删除当前项吗', null, async () => {
|
||||
const { msg } = await doDelete({ ids: row.id })
|
||||
$baseMessage(msg, 'success', 'vab-hey-message-success')
|
||||
await fetchData()
|
||||
})
|
||||
} else {
|
||||
if (state.selectRows.length > 0) {
|
||||
const ids = state.selectRows
|
||||
.map((item) => item.id)
|
||||
.join(',')
|
||||
$baseConfirm('你确定要删除选中项吗', null, async () => {
|
||||
const { msg } = await doDelete({ ids })
|
||||
$baseMessage(
|
||||
msg,
|
||||
'success',
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
await fetchData()
|
||||
})
|
||||
} else {
|
||||
$baseMessage(
|
||||
'未选中任何行',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dragOptions,
|
||||
finallyColumns,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
queryData,
|
||||
setSelectRows,
|
||||
clickFullScreen,
|
||||
fetchData,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
Plus,
|
||||
Delete,
|
||||
Search,
|
||||
Setting,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use 'sass:math';
|
||||
|
||||
.custom-table-container {
|
||||
:deep() {
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
.stripe-panel,
|
||||
.border-panel {
|
||||
margin: 0 10px #{math.div($base-margin, 2)} 10px !important;
|
||||
|
||||
:deep() {
|
||||
.el-checkbox__label {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class*='ri'] {
|
||||
font-size: $base-font-size-big;
|
||||
color: var(--el-color-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
html body .custom-table-checkbox {
|
||||
[class*='ri'] {
|
||||
vertical-align: -0.5px !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
margin: 5px 0 5px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-table-radio {
|
||||
width: 240px !important;
|
||||
}
|
||||
</style>
|
||||
98
front-end/src/views/vab/table/detail.vue
Normal file
98
front-end/src/views/vab/table/detail.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="detail-container">
|
||||
<el-page-header
|
||||
:content="'【' + route.query.title + '】详情页'"
|
||||
@back="goBack"
|
||||
/>
|
||||
<el-alert :closable="false" title="当前详情页允许多开" />
|
||||
<el-form inline :model="form" @submit.prevent>
|
||||
<el-form-item label="输入框缓存">
|
||||
<el-input v-model="form.text" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作">
|
||||
<el-button
|
||||
:icon="Refresh"
|
||||
type="primary"
|
||||
@click="handleRefreshMainPage"
|
||||
>
|
||||
刷新综合表格页面
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-descriptions border :column="2" title="详情">
|
||||
<el-descriptions-item>
|
||||
<template #label>标题</template>
|
||||
{{ route.query.title }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>作者</template>
|
||||
{{ route.query.author }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>时间</template>
|
||||
{{ route.query.datetime }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>评级</template>
|
||||
<el-rate v-model="rate" disabled />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>备注</template>
|
||||
{{ route.query.description }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Refresh } from '@element-plus/icons-vue'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
import { handleActivePath } from '@/utils/routes'
|
||||
|
||||
defineOptions({
|
||||
name: 'Detail',
|
||||
})
|
||||
|
||||
const route: any = useRoute()
|
||||
const $pub = inject<any>('$pub')
|
||||
const tabsStore = useTabsStore()
|
||||
const { changeTabsMeta, delVisitedRoute } = tabsStore
|
||||
const form = reactive<any>({ text: '' })
|
||||
const rate = ref<number>(Number.parseInt(route.query.rate))
|
||||
|
||||
const goBack = async () => {
|
||||
await delVisitedRoute(handleActivePath(route, true))
|
||||
history.back()
|
||||
}
|
||||
|
||||
const handleRefreshMainPage = () => {
|
||||
$pub('reload-router-view', 'ComprehensiveTable')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
changeTabsMeta({
|
||||
title: '详情页',
|
||||
meta: {
|
||||
title: `${route.query.title} 详情页`,
|
||||
},
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.default-table-detail-container {
|
||||
:deep() {
|
||||
.el-form--inline {
|
||||
.el-form-item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-descriptions__label {
|
||||
min-width: 80px !important;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
147
front-end/src/views/vab/table/dynamicTable.vue
Normal file
147
front-end/src/views/vab/table/dynamicTable.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="dynamic-table-container">
|
||||
<el-form ref="formRef" :model="form">
|
||||
<el-form-item :label-width="0" prop="list">
|
||||
<vab-form-table
|
||||
v-model="form.list"
|
||||
drag
|
||||
:row-template="rowTemplate"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column align="center" label="标题" prop="title">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.title" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="作者" prop="author">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.author" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="评级" prop="rate">
|
||||
<template #default="{ row }">
|
||||
<el-rate v-model="row.rate" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="点击量"
|
||||
prop="pageViews"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.pageViews"
|
||||
type="number"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="时间"
|
||||
prop="datetime"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-date-picker v-model="row.datetime" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="类型" prop="type">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.type" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in typeDic"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="开关"
|
||||
prop="switch"
|
||||
width="80"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.switch" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</vab-form-table>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Delete, Plus, Search } from '@element-plus/icons-vue'
|
||||
import VabFormTable from '@/plugins/VabFormTable'
|
||||
import { doEdit, getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicTable',
|
||||
components: { VabFormTable },
|
||||
setup() {
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
rowTemplate: {
|
||||
title: '',
|
||||
author: '',
|
||||
rate: 0,
|
||||
pageViews: 0,
|
||||
dateTime: '',
|
||||
type: 1,
|
||||
},
|
||||
form: {
|
||||
list: [],
|
||||
},
|
||||
typeDic: [
|
||||
{
|
||||
label: '热点',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '头条',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const save = () => {
|
||||
state.formRef.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { msg } = await doEdit(state.form)
|
||||
$baseMessage(msg, 'success', 'vab-hey-message-success')
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList({
|
||||
pageSize: 5,
|
||||
pageNo: 1,
|
||||
})
|
||||
state.form.list = list
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
save,
|
||||
Delete,
|
||||
Plus,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
176
front-end/src/views/vab/table/inlineEditTable.vue
Normal file
176
front-end/src/views/vab/table/inlineEditTable.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div class="inline-edit-table-container">
|
||||
<el-alert
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="三级路由【不缓存路由并且不固定表格高度】的示例"
|
||||
type="success"
|
||||
/>
|
||||
<vab-query-form>
|
||||
<vab-query-form-right-panel :span="24">
|
||||
<el-form inline :model="queryForm" @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="queryForm.title"
|
||||
placeholder="标题"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:icon="Search"
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="queryData"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-right-panel>
|
||||
</vab-query-form>
|
||||
<el-table v-loading="listLoading" border :data="list">
|
||||
<el-table-column
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
type="selection"
|
||||
width="55"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="序号"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="标题"
|
||||
min-width="300px"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.edit">
|
||||
<el-input v-model="row.title" style="width: 300px" />
|
||||
<el-button
|
||||
style="margin-left: 10px"
|
||||
@click="cancelEdit(row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-else>{{ row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="作者"
|
||||
prop="author"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.edit"
|
||||
type="primary"
|
||||
@click="confirmEdit(row)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
<el-button v-else @click="row.edit = !row.edit">
|
||||
编辑
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||
</template>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="queryForm.pageNo"
|
||||
:layout="layout"
|
||||
:page-size="queryForm.pageSize"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Edit, Search } from '@element-plus/icons-vue'
|
||||
import { getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InlineEditTable',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
list: [],
|
||||
listLoading: true,
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
total: 0,
|
||||
queryForm: {
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
title: '',
|
||||
},
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
state.listLoading = true
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await getList(state.queryForm)
|
||||
state.list = list.map((v) => {
|
||||
v.edit = false
|
||||
v.originalTitle = v.title
|
||||
return v
|
||||
})
|
||||
state.total = total
|
||||
state.listLoading = false
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
state.queryForm.pageSize = val
|
||||
fetchData()
|
||||
}
|
||||
const handleCurrentChange = (val) => {
|
||||
state.queryForm.pageNo = val
|
||||
fetchData()
|
||||
}
|
||||
const queryData = () => {
|
||||
state.queryForm.pageNo = 1
|
||||
fetchData()
|
||||
}
|
||||
const cancelEdit = (row) => {
|
||||
row.title = row.originalTitle
|
||||
row.edit = false
|
||||
}
|
||||
const confirmEdit = (row) => {
|
||||
row.edit = false
|
||||
row.originalTitle = row.title
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
queryData,
|
||||
cancelEdit,
|
||||
confirmEdit,
|
||||
Edit,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user