Commit d207265b authored by hucy's avatar hucy

fix:sortablejs

parent 0562c9f8
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -25,10 +25,12 @@ ...@@ -25,10 +25,12 @@
"mitt": "^3.0.0", "mitt": "^3.0.0",
"pinia": "^2.0.14", "pinia": "^2.0.14",
"quasar": "^2.6.0", "quasar": "^2.6.0",
"sortablejs": "^1.15.0",
"swiper": "^8.4.5", "swiper": "^8.4.5",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.0.0", "vue-router": "^4.0.0",
"vuedraggable": "^4.1.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.19.0/xlsx-0.19.0.tgz" "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.0/xlsx-0.19.0.tgz"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -29,12 +29,12 @@ const myDateRange = ref<any>(null); ...@@ -29,12 +29,12 @@ const myDateRange = ref<any>(null);
const dates = computed({ const dates = computed({
set(value: any) { set(value: any) {
console.log('set', value); // console.log('set', value);
emit('update:modelValue', value); emit('update:modelValue', value);
}, },
get() { get() {
console.log('get', props.modelValue); // console.log('get', props.modelValue);
let res = null; let res = null;
// 多选 // 多选
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<q-expansion-item <q-expansion-item
v-if="children && children.length > 0" v-if="children && children.length > 0"
expand-separator expand-separator
default-opened
:label="title" :label="title"
:caption="caption" :caption="caption"
switch-toggle-side switch-toggle-side
......
<template> <template>
<div>
<q-form ref="myForm" class="my-form row fit"> <q-form ref="myForm" class="my-form row fit">
<div <div
:class="['item', item.col || 'col-12']" :class="['item', item.col || 'col-12']"
...@@ -12,7 +13,10 @@ ...@@ -12,7 +13,10 @@
</template> </template>
<template v-else-if="item.type === 'password'"> <template v-else-if="item.type === 'password'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<q-input <q-input
...@@ -45,7 +49,10 @@ ...@@ -45,7 +49,10 @@
</template> </template>
<template v-else-if="item.type === 'date'"> <template v-else-if="item.type === 'date'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<date-time-pick <date-time-pick
...@@ -72,7 +79,10 @@ ...@@ -72,7 +79,10 @@
</template> </template>
<template v-else-if="item.type === 'time'"> <template v-else-if="item.type === 'time'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<time-pick <time-pick
...@@ -97,7 +107,10 @@ ...@@ -97,7 +107,10 @@
</template> </template>
<template v-else-if="item.type === 'dateMultiple'"> <template v-else-if="item.type === 'dateMultiple'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<date-multiple <date-multiple
...@@ -121,7 +134,10 @@ ...@@ -121,7 +134,10 @@
</template> </template>
<template v-else-if="item.type === 'dateRange'"> <template v-else-if="item.type === 'dateRange'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<date-range <date-range
...@@ -145,7 +161,10 @@ ...@@ -145,7 +161,10 @@
</template> </template>
<template v-else-if="item.type === 'color'"> <template v-else-if="item.type === 'color'">
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<color-pick <color-pick
...@@ -169,7 +188,10 @@ ...@@ -169,7 +188,10 @@
</template> </template>
<template v-else> <template v-else>
<div class="item-content"> <div class="item-content">
<div class="label-title" :class="{ 'text-required': item.required }"> <div
class="label-title"
:class="{ 'text-required': item.required }"
>
{{ item.label }} {{ item.label }}
</div> </div>
<q-input <q-input
...@@ -192,6 +214,7 @@ ...@@ -192,6 +214,7 @@
" "
/> />
<q-select <q-select
class="my-select"
v-if="item.type === 'select'" v-if="item.type === 'select'"
v-bind="item.bind" v-bind="item.bind"
v-model="formValue[item.fild]" v-model="formValue[item.fild]"
...@@ -213,6 +236,7 @@ ...@@ -213,6 +236,7 @@
</template> </template>
</div> </div>
</q-form> </q-form>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, reactive, ref, watch } from 'vue'; import { onMounted, reactive, ref, watch } from 'vue';
...@@ -326,4 +350,14 @@ function reset() { ...@@ -326,4 +350,14 @@ function reset() {
margin-bottom: 2px; margin-bottom: 2px;
display: inline-block; display: inline-block;
} }
:deep(.my-select
> :nth-child(1)
> :nth-child(1)
> :nth-child(1)
> :nth-child(1)
> span) {
white-space: nowrap;
overflow: hidden;
}
</style> </style>
...@@ -42,6 +42,13 @@ export const MenuList = [ ...@@ -42,6 +42,13 @@ export const MenuList = [
link: '/page10', link: '/page10',
active: false, active: false,
}, },
{
title: 'tree',
caption: '树',
icon: require('./menuListIcons/page10.svg'),
link: '/tree',
active: false,
},
{ {
title: '表单', title: '表单',
caption: '', caption: '',
...@@ -55,6 +62,13 @@ export const MenuList = [ ...@@ -55,6 +62,13 @@ export const MenuList = [
link: '/page2', link: '/page2',
active: false, active: false,
}, },
{
title: '信息',
caption: '表单表格测试',
icon: require('./menuListIcons/form-test.svg'),
link: '/form-test',
active: false,
},
], ],
}, },
{ {
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673405134523" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="977" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M30.819128 89.091116c0-1.609137 2.486849-3.267037 2.486849-4.876174 0 1.609137 0 3.267037-2.486849 4.876174z m-5.656362 28.428096v2.389325a107.617165 107.617165 0 0 0 0-2.389325z m807.494451-39.253202l0.633903 99.912809 153.404441-1.219043-0.585141-67.778822C985.476517 49.74039 933.78907-0.240395 873.129463 0.344746h-7.119214c6.534073 0 15.457472 2.974466 21.942784 4.193509-32.670367 7.119214-55.247054 38.6193-55.247054 73.727755z" fill="#BBE2FF" p-id="978"></path><path d="M832.705979 78.26601c0-35.108454 21.942784-66.023399 55.295815-73.727755-6.534073-1.219044-15.457472-4.19351-21.991545-4.193509L136.339538 6.34244C87.577796 6.927581 44.764986 40.231851 30.526557 84.214942c0 0.585141-0.633903 1.170282-0.633902 1.804185-0.585141 1.170282-0.585141 2.340564-1.170282 3.559607a124.683775 124.683775 0 0 0-3.559607 30.329803L25.747907 185.298034l807.494451-6.534074-0.536379-100.49795z" fill="#65AFE7" p-id="979"></path><path d="M898.095475 1012.492229a131.900513 131.900513 0 0 1-21.991546 1.755423h7.119215c60.659607-0.585141 111.810675-51.151068 111.176772-110.591632l-0.585141-67.778821-153.404441 1.170281 0.585141 104.642699c0.633903 35.108454 23.79573 63.634074 57.1 70.80205z" fill="#BBE2FF" p-id="980"></path><path d="M33.062168 844.31298L26.235524 167.402474l808.177116-5.753885 6.826643 676.910505z" fill="#3396FB" p-id="981"></path><path d="M33.452262 911.360375c0.633903 59.489326 52.370111 109.42135 112.980957 108.836209l729.67071-5.948932c6.534073 0 15.457472-0.585141 21.991546-1.755423a71.045858 71.045858 0 0 1-56.466098-70.80205l-0.633902-104.642699-807.494451 6.534074v67.778821z" fill="#65AFE7" p-id="982"></path><path d="M218.259265 334.801535h-1.804185A45.689752 45.689752 0 0 1 170.667804 289.5994C170.667804 264.633388 190.903927 243.812124 215.869939 243.812124h1.755423c25.014774 0 45.836038 20.236123 45.836038 45.202135a45.689752 45.689752 0 0 1-45.202135 45.787276z" fill="#FFFFFF" p-id="983"></path><path d="M599.234757 710.461998h-49.054313a38.424253 38.424253 0 0 1-38.277967-38.277968c0-21.016311 17.261657-38.277968 38.277967-38.277968h49.054313c21.016311 0 38.277968 17.261657 38.277967 38.277968a38.034159 38.034159 0 0 1-38.277967 38.277968zM404.090264 710.461998H208.945772A38.424253 38.424253 0 0 1 170.667804 672.18403c0-21.016311 17.261657-38.277968 38.277968-38.277968h195.144492c21.016311 0 38.277968 17.261657 38.277968 38.277968a38.424253 38.424253 0 0 1-38.277968 38.277968z" fill="#FFB541" p-id="984"></path><path d="M404.090264 629.029888H208.945772a43.300427 43.300427 0 0 0-43.154142 43.154142c0 23.698207 19.455935 43.154142 43.154142 43.154142h195.144492a43.300427 43.300427 0 0 0 43.154142-43.056619 43.300427 43.300427 0 0 0-43.154142-43.251665z m-195.144492 9.752348h195.144492c18.334415 0 33.401793 15.067378 33.401794 33.401794a33.499317 33.499317 0 0 1-33.401794 33.401793H208.945772a33.548079 33.548079 0 0 1-33.401793-33.401793c0-18.334415 15.067378-33.401793 33.401793-33.401794zM599.234757 629.029888h-49.054313a43.300427 43.300427 0 0 0-43.154142 43.154142c0 23.698207 19.455935 43.154142 43.154142 43.154142h49.054313a42.910333 42.910333 0 0 0 43.154142-43.154142 43.300427 43.300427 0 0 0-43.154142-43.154142z m-49.054313 9.752348h49.054313c18.334415 0 33.401793 15.067378 33.401793 33.401794a33.157985 33.157985 0 0 1-33.401793 33.401793h-49.054313a33.548079 33.548079 0 0 1-33.401793-33.401793c0-18.334415 15.067378-33.401793 33.401793-33.401794z" fill="#FFFFFF" p-id="985"></path><path d="M208.945772 438.859093h411.061487a38.277968 38.277968 0 0 1 0 76.555936h-411.061487a38.277968 38.277968 0 0 1 0-76.555936z" fill="#FFB541" p-id="986"></path><path d="M620.007259 433.982919h-411.061487a43.154142 43.154142 0 0 0 0 86.308284h411.061487a43.154142 43.154142 0 0 0 0-86.308284z m-411.061487 9.752349h411.061487a33.401793 33.401793 0 1 1 0 66.803586h-411.061487a33.401793 33.401793 0 1 1 0-66.803586z" fill="#FFFFFF" p-id="987"></path></svg>
\ No newline at end of file
<!--
* 表单 表格
-->
<script lang="ts">
export default {
name: 'FORM_TEST',
};
</script>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { AgGridVue } from 'ag-grid-vue3';
import { cloneDeep } from 'src/common/utils';
import AG_GRID_LOCALE from 'src/config/ag-grid-locale';
import { defaultColDef, testData } from './config';
import FormDialog from './element/FormDialog.vue';
const formDialogRef = ref<any>(null);
const gridApi = ref<any>(null);
const gridColumnApi = ref<any>(null);
const columnDefs = reactive([
{ field: 'name', headerName: '姓名' },
{ field: 'age', headerName: '年龄' },
]);
const state = reactive({
rowData: [] as any,
});
function onGridReady(params: any) {
gridApi.value = params.api;
gridColumnApi.value = params.columnApi;
getData();
}
function getData() {
state.rowData = cloneDeep(testData);
}
function clickAdd() {
formDialogRef.value.openDialog();
}
</script>
<template>
<div class="box">
<div class="actions">
<q-btn color="primary" label="添加" @click="clickAdd" />
</div>
<div class="ag-table">
<ag-grid-vue
style="height: 550px"
class="ag-theme-alpine"
:animateRows="true"
:localeText="AG_GRID_LOCALE"
:suppressContextMenu="true"
:suppressCellFocus="true"
:defaultColDef="defaultColDef"
:columnDefs="columnDefs"
:rowData="state.rowData"
@grid-ready="onGridReady"
>
</ag-grid-vue>
</div>
<form-dialog ref="formDialogRef" />
</div>
</template>
<style lang="scss" scoped>
.actions {
padding: 10px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
column-gap: 10px;
}
.ag-table {
padding: 0 10px 10px 10px;
}
</style>
import { isEmpty } from 'src/common/utils';
export const defaultColDef = {
flex: 1,
minWidth: 100,
suppressMenu: true, // 是否禁用标题行菜单
};
export const testData = [
{ name: '张三', age: 22 },
{ name: '孙火旺', age: 18 },
];
export const form_config = [
{
fild: 'username',
label: '用户名',
col: 'col-4',
type: 'text',
required: true,
bind: {
filled: true,
lazyRules: true,
hideBottomSpace: true,
rules: [(val: any) => !isEmpty(val) || '必填'],
},
},
{
fild: 'account',
label: '账号',
col: 'col-4',
type: 'text',
required: true,
bind: {
filled: true,
lazyRules: true,
hideBottomSpace: true,
rules: [(val: any) => !isEmpty(val) || '必填'],
},
},
{
fild: 'age',
label: '年龄',
col: 'col-4',
type: 'number',
required: true,
bind: {
filled: true,
lazyRules: true,
hideBottomSpace: true,
rules: [
(val: any) => !isEmpty(val) || '必填',
(val: any) => (val > 0 && val < 100) || '请输入真实年龄',
],
},
},
{
fild: 'sex',
label: '性别',
col: 'col-4',
type: 'select',
required: true,
bind: {
filled: true,
lazyRules: true,
hideBottomSpace: true,
rules: [(val: any) => !isEmpty(val) || '必填'],
emitValue: true,
mapOptions: true,
options: [
{
label: '男',
value: 1,
},
{
label: '女',
value: 0,
},
],
},
},
{
fild: 'dateRange',
label: '开始日期~结束日期',
col: 'col-4',
type: 'dateRange',
bind: {
filled: true,
clearable: true,
dateMask: 'YYYY-MM-DD',
},
},
{
fild: 'platform',
label: '平台',
col: 'col-4',
type: 'select',
bind: {
filled: true,
multiple: true,
clearable: true,
emitValue: true,
mapOptions: true,
options: [
{
label: '微信',
value: 'WeChat',
},
{
label: '微博',
value: 'Microblog',
},
{
label: '哔哩哔哩',
value: 'bilibili',
},
{
label: 'Twitter',
value: 'Twitter',
},
{
label: 'Facebook',
value: 'Facebook',
},
],
},
},
{
fild: 'remark',
label: '备注',
col: 'col-12',
type: 'textarea',
bind: {
filled: true,
},
},
];
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import Com from 'src/components';
import { form_config } from '../config';
defineExpose({
openDialog,
closeDialog,
});
const formRef = ref<any>(null);
const show = ref(false);
const state = reactive({
readonly: false,
formConfig: [] as any[],
formData: {} as any,
});
onMounted(() => {
initFormConfig();
});
function initDialog() {
state.formData = {};
}
function initFormConfig() {
state.formConfig = [...form_config];
}
function openDialog() {
show.value = true;
}
function closeDialog() {
show.value = false;
initDialog();
}
function onCancel() {
closeDialog();
}
function onSave() {
formRef.value.validate().then((suc: any) => {
if (!suc) return;
console.log('formData >>>>', state.formData);
closeDialog();
});
}
</script>
<template>
<q-dialog v-model="show" persistent>
<q-card class="my-card">
<div class="card-content">
<div class="title">添加</div>
<div class="content">
<Com.MyForm
ref="formRef"
dense
:readonly="state.readonly"
:config="state.formConfig"
v-model="state.formData"
></Com.MyForm>
</div>
<div class="bottom-action">
<q-btn flat style="color: #64b3f4" label="取消" @click="onCancel" />
<q-btn
unelevated
style="background: #64b3f4; color: white"
label="确定"
@click="onSave()"
/>
</div>
</div>
</q-card>
</q-dialog>
</template>
<style lang="scss" scoped>
.my-card {
width: 1000px;
max-width: 1000px;
min-height: 500px;
padding: 20px;
background-image: linear-gradient(60deg, #c2e59c 0%, #64b3f4 100%);
box-sizing: border-box;
display: flex;
}
.card-content {
width: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
.title {
height: 40px;
padding: 8px;
font-size: 16px;
font-weight: bolder;
color: #c2e59c;
display: flex;
align-items: center;
// background-color: #ee9ca7;
}
.content {
flex: 1;
padding: 8px;
}
.bottom-action {
// background-color: #209cff;
padding: 8px;
display: flex;
flex-direction: row;
column-gap: 4px;
justify-content: flex-end;
align-items: center;
}
}
</style>
export default [
{
path: 'form-test',
name: 'FORM_TEST',
component: () => import('./IndexPage.vue'),
meta: {
title: '表单&表格',
permission: ['*'],
keepalive: true,
},
},
];
...@@ -197,6 +197,30 @@ function onChange() { ...@@ -197,6 +197,30 @@ function onChange() {
label: '听音乐', label: '听音乐',
value: 'listen_music', value: 'listen_music',
}, },
{
label: '微信',
value: 'WeChat',
},
{
label: '微博',
value: 'Microblog',
},
{
label: '测试1',
value: '1',
},
{
label: '测试2',
value: '2',
},
{
label: '测试3',
value: '3',
},
{
label: '测试4',
value: '4',
},
]; ];
} }
return item; return item;
......
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import nestedDraggable from './infra/nested.vue';
const state = reactive({
list: [] as any[],
});
onMounted(() => {
state.list = [
{
name: 'task 1',
tasks: [
{
name: 'task 2',
tasks: [],
},
],
},
{
name: 'task 3',
tasks: [
{
name: 'task 4',
tasks: [],
},
],
},
{
name: 'task 5',
tasks: [],
},
];
});
</script>
<template>
<div class="box">
<div class="tree-box">
<h3>标题</h3>
<nested-draggable :tasks="state.list" />
</div>
</div>
</template>
<style lang="scss" scoped>
.tree-box {
width: 600px;
box-sizing: border-box;
border: 1px solid brown;
}
</style>
<template>
<draggable
class="dragArea"
tag="ul"
:list="tasks"
:group="{ name: 'g1' }"
item-key="name"
>
<template #item="{ element }">
<li>
<p>{{ element.name }}</p>
<nested-draggable :tasks="element.tasks" />
</li>
</template>
</draggable>
</template>
<script>
import draggable from 'vuedraggable';
export default {
props: {
tasks: {
required: true,
type: Array,
},
},
components: {
draggable,
},
name: 'nested-draggable',
};
</script>
<style scoped>
.dragArea {
min-height: 50px;
/* outline: 1px dashed; */
border: 1px solid red;
}
</style>
export default [
{
path: 'tree',
name: 'TREE',
component: () => import('./IndexPage.vue'),
meta: {
title: '树',
permission: ['*'],
keepalive: true,
},
},
];
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import FORM_TEST from '../modules/form-test/route';
import TREE from '../modules/tree/route';
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
path: '/', path: '/',
...@@ -10,7 +13,7 @@ const routes: RouteRecordRaw[] = [ ...@@ -10,7 +13,7 @@ const routes: RouteRecordRaw[] = [
path: '', path: '',
name: 'LaoutIndexPage', name: 'LaoutIndexPage',
component: () => import('pages/IndexPage.vue'), component: () => import('pages/IndexPage.vue'),
redirect: '/page9', redirect: '/tree',
children: [ children: [
{ {
path: 'home', path: 'home',
...@@ -122,6 +125,8 @@ const routes: RouteRecordRaw[] = [ ...@@ -122,6 +125,8 @@ const routes: RouteRecordRaw[] = [
keepalive: true, keepalive: true,
}, },
}, },
...FORM_TEST,
...TREE,
], ],
}, },
], ],
......
declare module 'animejs/lib/anime.es.js'; declare module 'animejs/lib/anime.es.js';
declare module 'swiper/bundle'; declare module 'swiper/bundle';
declare module 'sortablejs';
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment