chore: update dependencies

This commit is contained in:
0xJacky 2022-11-12 23:38:46 +08:00
parent 33064693a6
commit 813b94baae
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
13 changed files with 1155 additions and 306 deletions

View file

@ -58,11 +58,12 @@ declare module '@vue/runtime-core' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SetLanguage: typeof import('./src/components/SetLanguage/SetLanguage.vue')['default']
StdBatchEdit: typeof import('./src/components/StdDataDisplay/StdBatchEdit.vue')['default']
StdCurd: typeof import('./src/components/StdDataDisplay/StdCurd.vue')['default']
StdPagination: typeof import('./src/components/StdDataDisplay/StdPagination.vue')['default']
StdPassword: typeof import('./src/components/StdDataEntry/compontents/StdPassword.vue')['default']
StdSelect: typeof import('./src/components/StdDataEntry/compontents/StdSelect.vue')['default']
StdSelector: typeof import('./src/components/StdDataEntry/compontents/StdSelector.vue')['default']
StdPassword: typeof import('./src/components/StdDataEntry/components/StdPassword.vue')['default']
StdSelect: typeof import('./src/components/StdDataEntry/components/StdSelect.vue')['default']
StdSelector: typeof import('./src/components/StdDataEntry/components/StdSelector.vue')['default']
StdTable: typeof import('./src/components/StdDataDisplay/StdTable.vue')['default']
}
}

View file

@ -12,34 +12,35 @@
},
"dependencies": {
"@ant-design/icons-vue": "^6.1.0",
"ant-design-vue": "^3.2.13",
"apexcharts": "^3.35.4",
"axios": "^0.27.2",
"dayjs": "^1.11.4",
"lodash": "^4.17.21",
"ant-design-vue": "^3.2.15",
"apexcharts": "^3.36.3",
"axios": "^1.1.3",
"dayjs": "^1.11.6",
"pinia": "^2.0.23",
"pinia-plugin-persistedstate": "^1.6.3",
"pinia-plugin-persistedstate": "^2.3.0",
"reconnecting-websocket": "^4.4.0",
"vite-plugin-build-id": "^0.2.2",
"vue": "^3.2.41",
"vue": "^3.2.45",
"vue-router": "4",
"vue3-ace-editor": "^2.2.2",
"vue3-apexcharts": "^1.4.1",
"vue3-gettext": "^2.3.4",
"xterm": "^4.19.0",
"xterm-addon-attach": "^0.6.0",
"xterm-addon-fit": "^0.5.0"
"xterm": "^5.0.0",
"xterm-addon-attach": "^0.7.0",
"xterm-addon-fit": "^0.6.0",
"@types/lodash": "^4.14.188",
"vuedraggable": "^4.1.0",
"@types/sortablejs": "^1.15.0"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@vitejs/plugin-vue": "^3.0.0",
"@vitejs/plugin-vue-jsx": "^2.0.0",
"@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.1",
"@zougt/vite-plugin-theme-preprocessor": "^1.4.5",
"less": "^4.1.3",
"typescript": "^4.6.4",
"unplugin-vue-components": "^0.21.2",
"vite": "^3.0.0",
"unplugin-vue-components": "^0.22.9",
"vite": "^3.2.3",
"vite-plugin-html": "^3.2.0",
"vue-tsc": "^0.38.4"
"vue-tsc": "^1.0.9"
}
}

View file

@ -0,0 +1,77 @@
<script setup lang="ts">
import {reactive, ref} from 'vue'
import gettext from '@/gettext'
const {$gettext} = gettext
import StdDataEntry from '@/components/StdDataEntry'
import {message} from 'ant-design-vue'
const emit = defineEmits(['onSave'])
const props = defineProps(['api', 'beforeSave'])
const batchColumns = ref([])
const visible = ref(false)
const selectedRowKeys = ref([])
function showModal(c: any, rowKeys: any) {
visible.value = true
selectedRowKeys.value = rowKeys
batchColumns.value = c
}
defineExpose({
showModal
})
const data = reactive({})
const error = reactive({})
const loading = ref(false)
async function ok() {
loading.value = true
await props.beforeSave?.()
await props.api(selectedRowKeys.value, data).then(async () => {
message.success($gettext('Save successfully'))
emit('onSave')
}).catch((e: any) => {
message.error(e?.message ?? $gettext('Server error'))
}).finally(() => {
loading.value = false
})
}
</script>
<template>
<a-modal
class="std-curd-edit-modal"
:mask="false"
:title="$gettext('Batch Modify')"
v-model:visible="visible"
:cancel-text="$gettext('Cancel')"
:ok-text="$gettext('OK')"
@ok="ok"
:confirm-loading="loading"
:width="600"
destroyOnClose
>
<std-data-entry
ref="std_data_entry"
:data-list="batchColumns"
v-model:data-source="data"
:error="error"
/>
<slot name="extra"/>
</a-modal>
</template>
<style scoped>
</style>

View file

@ -55,17 +55,18 @@ const props = defineProps({
modalWidth: {
type: Number,
default: 600
}
},
useSortable: Boolean
})
const visible = ref(false)
const update = ref(0)
const data: any = reactive({id: null})
const error: any = reactive({})
const selected = reactive([])
const selected = ref([])
function onSelect(keys: any) {
selected.concat(...keys)
selected.value = keys
}
function editableColumns() {
@ -83,6 +84,11 @@ function add() {
visible.value = true
}
defineExpose({
add,
data
})
const table = ref(null)
interface Table {
@ -125,6 +131,7 @@ function edit(id: any) {
})
}
const selectedRowKeys = ref([])
</script>
<template>
@ -136,12 +143,11 @@ function edit(id: any) {
<std-table
ref="table"
v-model:selected-row-keys="selectedRowKeys"
v-bind="props"
@clickEdit="edit"
@selected="onSelect"
:key="update"
:get_params="get_params"
:exportCsv="exportCsv"
>
<template v-slot:actions="slotProps">
<slot name="actions" :actions="slotProps.record"/>

View file

@ -2,11 +2,11 @@
import {useGettext} from 'vue3-gettext'
const {pagination, size} = defineProps(['pagination', 'size'])
const emit = defineEmits(['changePage'])
const emit = defineEmits(['change'])
const {$gettext} = useGettext()
function changePage(num: number) {
emit('changePage', num)
function change(num: number, pageSize: number) {
emit('change', num, pageSize)
}
</script>
@ -17,7 +17,7 @@ function changePage(num: number) {
:pageSize="pagination.per_page"
:size="size"
:total="pagination.total"
@change="changePage"
@change="change"
/>
</div>
</template>

View file

@ -7,10 +7,13 @@ import {useRoute, useRouter} from 'vue-router'
import {message} from 'ant-design-vue'
import {downloadCsv} from '@/lib/helper'
import dayjs from 'dayjs'
import Sortable from 'sortablejs'
import {HolderOutlined} from '@ant-design/icons-vue'
import {toRaw} from '@vue/reactivity'
const {$gettext, interpolate} = gettext
const emit = defineEmits(['onSelected', 'onSelectedRecord', 'clickEdit', 'update:selectedRowKeys'])
const emit = defineEmits(['onSelected', 'onSelectedRecord', 'clickEdit', 'update:selectedRowKeys', 'clickBatchModify'])
const props = defineProps({
api: Object,
@ -71,11 +74,14 @@ const props = defineProps({
size: String,
selectedRowKeys: {
type: Array
}
},
useSortable: Boolean
})
const data_source: any = ref([])
const expand_keys_list: any = ref([])
const rows_key_index_map: any = ref({})
const data_source = ref([])
const loading = ref(true)
const pagination = reactive({
total: 1,
@ -83,6 +89,7 @@ const pagination = reactive({
current_page: 1,
total_pages: 1
})
const route = useRoute()
const params = reactive({
...props.get_params
@ -102,12 +109,17 @@ const selectedRowKeysBuffer = computed({
const searchColumns = getSearchColumns()
const pithyColumns = getPithyColumns()
const batchColumns = getBatchEditColumns()
onMounted(() => {
if (!props.disable_query_params) {
Object.assign(params, route.query)
}
get_list()
if (props.useSortable) {
initSortable()
}
})
defineExpose({
@ -123,13 +135,29 @@ function destroy(id: any) {
})
}
function get_list(page_num = null) {
function get_list(page_num = null, page_size = 20) {
loading.value = true
if (page_num) {
params['page'] = page_num
params['page_size'] = page_size
}
props.api!.get_list(params).then((r: any) => {
props.api!.get_list(params).then(async (r: any) => {
data_source.value = r.data
rows_key_index_map.value = {}
if (props.useSortable) {
function buildIndexMap(data: any, level: number = 0, index: number = 0, total: number[] = []) {
if (data && data.length > 0) {
data.forEach((v: any) => {
v.level = level
let current_index = [...total, index++]
rows_key_index_map.value[v.id] = current_index
if (v.children) buildIndexMap(v.children, level + 1, 0, current_index)
})
}
}
buildIndexMap(r.data)
}
if (r.pagination !== undefined) {
Object.assign(pagination, r.pagination)
@ -159,6 +187,10 @@ function stdChange(pagination: any, filters: any, sorter: any) {
}
}
function expandedTable(keys: any) {
expand_keys_list.value = keys
}
function getSearchColumns() {
let searchColumns: any = []
props.columns!.forEach((column: any) => {
@ -169,6 +201,16 @@ function getSearchColumns() {
return searchColumns
}
function getBatchEditColumns() {
let batch: any = []
props.columns!.forEach((column: any) => {
if (column.batch) {
batch.push(column)
}
})
return batch
}
function getPithyColumns() {
if (props.pithy) {
return props.columns!.filter((c: any, index: any, columns: any) => {
@ -187,7 +229,6 @@ function checked(c: any) {
const crossPageSelect: any = {}
async function onSelectChange(_selectedRowKeys: any) {
const page = params.page || 1
crossPageSelect[page] = await _selectedRowKeys
@ -231,10 +272,10 @@ watch(params, () => {
})
const rowSelection = computed(() => {
if (props.selectionType) {
if (batchColumns.length > 0 || props.selectionType) {
return {
selectedRowKeys: selectedRowKeysBuffer.value, onChange: onSelectChange,
onSelect: onSelect, type: props.selectionType
onSelect: onSelect, type: batchColumns.length > 0 ? 'checkbox' : props.selectionType
}
} else {
return null
@ -316,6 +357,112 @@ async function export_csv() {
downloadCsv(header, data,
`${$gettext('Export')}-${dayjs().format('YYYYMMDDHHmmss')}.csv`)
}
const hasSelectedRow = computed(() => {
return batchColumns.length > 0 && selectedRowKeysBuffer.value.length > 0
})
function click_batch_edit() {
emit('clickBatchModify', batchColumns, selectedRowKeysBuffer.value)
}
function getLeastIndex(index: number) {
return index >= 1 ? index : 1
}
function getTargetData(data: any, indexList: number[]): any {
let target: any = {children: data}
indexList.forEach((index: number) => {
target.children[index].parent = target
target = target.children[index]
})
return target
}
function initSortable() {
const table: any = document.querySelector('#std-table tbody')
new Sortable(table, {
handle: '.ant-table-drag-icon',
animation: 150,
sort: true,
forceFallback: true,
setData: function (dataTransfer) {
dataTransfer.setData('Text', '')
},
onStart({item}) {
let targetRowKey = Number(item.dataset.rowKey)
if (targetRowKey) {
expand_keys_list.value = expand_keys_list.value.filter((item: number) => item !== targetRowKey)
}
},
onMove({dragged, related}) {
const oldRow: number[] = rows_key_index_map.value?.[Number(dragged.dataset.rowKey)]
const newRow: number[] = rows_key_index_map.value?.[Number(related.dataset.rowKey)]
if (oldRow.length !== newRow.length || oldRow[oldRow.length - 2] != newRow[newRow.length - 2]) {
return false
}
},
async onEnd({item, newIndex, oldIndex}) {
if (newIndex === oldIndex) return
const indexDelta: number = Number(oldIndex) - Number(newIndex)
const direction: number = indexDelta > 0 ? +1 : -1
let rowIndex: number[] = rows_key_index_map.value?.[Number(item.dataset.rowKey)]
const newRow = getTargetData(data_source.value, rowIndex)
const newRowParent = newRow.parent
const level: number = newRow.level
let currentRowIndex: number[] = [...rows_key_index_map.value?.
[Number(table.children[Number(newIndex) + direction].dataset.rowKey)]]
let currentRow: any = getTargetData(data_source.value, currentRowIndex)
// Reset parent
currentRow.parent = newRow.parent = null
newRowParent.children.splice(rowIndex[level], 1)
newRowParent.children.splice(currentRowIndex[level], 0, toRaw(newRow))
let changeIds: number[] = []
function processChanges(row: any, children: boolean = false, newIndex: number | undefined = undefined) {
// Build changes ID list expect new row
if (children || newIndex === undefined) changeIds.push(row.id)
if (newIndex !== undefined)
rows_key_index_map.value[row.id][level] = newIndex
else if (children)
rows_key_index_map.value[row.id][level] += direction
row.parent = null
if (row.children) {
row.children.forEach((v: any) => processChanges(v, true, newIndex))
}
}
// Replace row index for new row
processChanges(newRow, false, currentRowIndex[level])
// Rebuild row index maps for changes row
for (let i = Number(oldIndex); i != newIndex; i -= direction) {
let rowIndex: number[] = rows_key_index_map.value?.[table.children[i].dataset.rowKey]
rowIndex[level] += direction
processChanges(getTargetData(data_source.value, rowIndex))
}
console.log('Change row id', newRow.id, 'order', newRow.id, '=>', currentRow.id, ', direction: ', direction,
', changes IDs:', changeIds)
props.api!.update_order({
target_id: newRow.id,
direction: direction,
affected_ids: changeIds
}).then(() => {
message.success($gettext('Updated successfully'))
}).catch((e: any) => {
message.error(e?.message ?? $gettext('Server error'))
})
}
})
}
</script>
<template>
@ -327,13 +474,16 @@ async function export_csv() {
layout="inline"
>
<template #action>
<a-space class="reset-btn">
<a-space class="action-btn">
<a-button v-if="exportCsv" @click="export_csv" type="primary" ghost>
{{ $gettext('Export') }}
</a-button>
<a-button @click="reset_search">
{{ $gettext('Reset') }}
</a-button>
<a-button v-if="hasSelectedRow" @click="click_batch_edit">
{{ $gettext('Batch Modify') }}
</a-button>
</a-space>
</template>
</std-data-entry>
@ -347,10 +497,17 @@ async function export_csv() {
@change="stdChange"
:scroll="{ x: scrollX }"
:size="size"
id="std-table"
@expandedRowsChange="expandedTable"
:expandedRowKeys="expand_keys_list"
>
<template
v-slot:bodyCell="{text, record, index, column}"
>
<template v-if="column.handle === true">
<span class="ant-table-drag-icon"><HolderOutlined/></span>
{{ text }}
</template>
<template v-if="column.dataIndex === 'action'">
<a v-if="props.editable" @click="$emit('clickEdit', record[props.rowKey], record)">
{{ props.edit_text || $gettext('Modify') }}
@ -361,7 +518,7 @@ async function export_csv() {
<a-popconfirm
:cancelText="$gettext('No')"
:okText="$gettext('OK')"
:title="$gettext('Are you sure you want to delete ?')"
:title="$gettext('Are you sure you want to delete?')"
@confirm="destroy(record[rowKey])">
<a v-translate>Delete</a>
</a-popconfirm>
@ -369,7 +526,7 @@ async function export_csv() {
</template>
</template>
</a-table>
<std-pagination :size="size" :pagination="pagination" @changePage="get_list"/>
<std-pagination :size="size" :pagination="pagination" @change="get_list"/>
</div>
</template>
@ -396,7 +553,7 @@ async function export_csv() {
}
}
.reset-btn {
.action-btn {
// min-height: 50px;
height: 100%;
display: flex;
@ -407,3 +564,15 @@ async function export_csv() {
margin-bottom: 10px;
}
</style>
<style lang="less">
.ant-table-drag-icon {
float: left;
margin-right: 16px;
cursor: grab;
}
.sortable-ghost *, .sortable-chosen * {
cursor: grabbing !important;
}
</style>

View file

@ -2,6 +2,7 @@
import dayjs from 'dayjs'
export interface customRender {
value: any
text: any
record: any
index: any

View file

@ -11,7 +11,7 @@ export default defineComponent({
props.dataList.forEach((v: any) => {
if (v.edit.type) {
template.push(
<FormItem label={v.title()}>
<FormItem label={v.title()} extra={v.extra}>
{v.edit.type(v.edit, props.dataSource, v.dataIndex)}
</FormItem>
)

View file

@ -0,0 +1,51 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
const props = defineProps(['value', 'generate', 'placeholder'])
const emit = defineEmits(['update:value'])
const M_value = computed({
get() {
return props.value
},
set(v) {
emit('update:value', v)
}
})
const visibility = ref(false)
function handle_generate() {
visibility.value = true
M_value.value = 'xxxx'
const chars = '0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const passwordLength = 12
let password = ''
for (let i = 0; i <= passwordLength; i++) {
const randomNumber = Math.floor(Math.random() * chars.length)
password += chars.substring(randomNumber, randomNumber + 1)
}
M_value.value = password
}
</script>
<template>
<a-input-group compact>
<a-input-password
v-if="!visibility"
:class="{compact: generate}"
v-model:value="M_value" :placeholoder="placeholder"/>
<a-input v-else :class="{compact: generate}" v-model:value="M_value" :placeholoder="placeholder"/>
<a-button @click="handle_generate" v-if="generate" type="primary">
<translate>Generate</translate>
</a-button>
</a-input-group>
</template>
<style scoped>
.compact {
width: calc(100% - 91px)
}
</style>

View file

@ -0,0 +1,45 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
import {SelectProps} from 'ant-design-vue'
const props = defineProps(['value', 'mask'])
const emit = defineEmits(['update:value'])
const options = computed(() => {
const _options = ref<SelectProps['options']>([])
for (const [key, value] of Object.entries(props.mask)) {
const v = value as any
_options.value!.push({label: v?.(), value: key})
}
return _options
})
const _value = computed({
get() {
let v
if (typeof props.mask?.[props.value] === 'function') {
v = props.mask[props.value]()
} else if (typeof props.mask?.[props.value] === 'string') {
v = props.mask[props.value]
} else {
v = props.value
}
return v
},
set(v) {
emit('update:value', v)
}
})
</script>
<template>
<a-select v-model:value="_value"
:options="options.value" style="min-width: 180px"/>
</template>
<style lang="less" scoped>
</style>

View file

@ -0,0 +1,146 @@
<script setup lang="ts">
import {computed, onMounted, reactive, ref, watch} from 'vue'
import StdTable from '@/components/StdDataDisplay/StdTable.vue'
import gettext from '@/gettext'
const {$gettext} = gettext
const props = defineProps(['selectedKey', 'value', 'recordValueIndex',
'selectionType', 'api', 'columns', 'data_key',
'disable_search', 'get_params', 'description'])
const emit = defineEmits(['update:selectedKey', 'changeSelect'])
const visible = ref(false)
const M_value = ref('')
onMounted(() => {
init()
})
const selected = ref([])
const record: any = reactive({})
function init() {
if (props.selectedKey && !props.value && props.selectionType === 'radio') {
props.api.get(props.selectedKey).then((r: any) => {
Object.assign(record, r)
M_value.value = r[props.recordValueIndex]
})
}
}
function show() {
visible.value = true
}
function onSelect(_selected: any) {
selected.value = _selected
}
function onSelectedRecord(r: any) {
Object.assign(record, r)
}
function ok() {
visible.value = false
if (props.selectionType == 'radio') {
emit('update:selectedKey', selected.value[0])
} else {
emit('update:selectedKey', selected.value)
}
M_value.value = record[props.recordValueIndex]
emit('changeSelect', record)
}
watch(props, () => {
if (!props?.selectedKey) {
M_value.value = ''
} else if (props.value) {
M_value.value = props.value
} else {
init()
}
})
const _selectedKey = computed({
get() {
return props.selectedKey
},
set(v) {
emit('update:selectedKey', v)
}
})
</script>
<template>
<div class="std-selector-container">
<div class="std-selector" @click="show()">
<a-input v-model="_selectedKey" disabled hidden/>
<div class="value">
{{ M_value }}
</div>
<a-modal
:mask="false"
:visible="visible"
:cancel-text="$gettext('Cancel')"
:ok-text="$gettext('OK')"
:title="$gettext('Selector')"
@cancel="visible=false"
@ok="ok()"
:width="800"
destroyOnClose
>
{{ description }}
<std-table
:api="api"
:columns="columns"
:data_key="data_key"
:disable_search="disable_search"
:pithy="true"
:get_params="get_params"
:selectionType="selectionType"
:disable_query_params="true"
@onSelected="onSelect"
@onSelectedRecord="onSelectedRecord"
/>
</a-modal>
</div>
</div>
</template>
<style lang="less" scoped>
.std-selector-container {
height: 39.9px;
display: flex;
align-items: flex-start;
.std-selector {
box-sizing: border-box;
font-variant: tabular-nums;
list-style: none;
font-feature-settings: 'tnum';
height: 32px;
padding: 4px 11px;
color: rgba(0, 0, 0, 0.85);
font-size: 14px;
line-height: 1.5;
background-color: #fff;
background-image: none;
border: 1px solid #d9d9d9;
border-radius: 4px;
transition: all 0.3s;
margin: 0 10px 0 0;
cursor: pointer;
min-width: 180px;
@media (prefers-color-scheme: dark) {
background-color: #1e1f20;
border: 1px solid #666666;
color: rgba(255, 255, 255, 0.99);
}
.value {
}
}
}
</style>

View file

@ -1,9 +1,9 @@
import StdDataEntry from './StdDataEntry.js'
import {h} from 'vue'
import {Input, Textarea, InputPassword} from 'ant-design-vue'
import StdSelector from './compontents/StdSelector.vue'
import StdSelect from './compontents/StdSelect.vue'
import StdPassword from './compontents/StdPassword.vue'
import {Input, Textarea, InputPassword, InputNumber} from 'ant-design-vue'
import StdSelector from './components/StdSelector.vue'
import StdSelect from './components/StdSelect.vue'
import StdPassword from './components/StdPassword.vue'
interface IEdit {
type: Function
@ -20,6 +20,9 @@ interface IEdit {
get_params: Object,
description: string
generate: boolean
min: number
max: number,
extra: string
}
function fn(obj: Object, desc: any) {
@ -55,6 +58,18 @@ function input(edit: IEdit, dataSource: any, dataIndex: any) {
})
}
function inputNumber(edit: IEdit, dataSource: any, dataIndex: any) {
return h(InputNumber, {
placeholder: edit.placeholder?.() ?? '',
min: edit.min,
max: edit.max,
value: dataSource?.[dataIndex],
'onUpdate:value': value => {
dataSource[dataIndex] = value
}
})
}
function textarea(edit: IEdit, dataSource: any, dataIndex: any) {
return h(Textarea, {
placeholder: edit.placeholder?.() ?? '',
@ -101,7 +116,8 @@ export {
textarea,
select,
selector,
password
password,
inputNumber
}
export default StdDataEntry

File diff suppressed because it is too large Load diff