mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 10:25:52 +02:00
chore: update dependencies
This commit is contained in:
parent
33064693a6
commit
813b94baae
13 changed files with 1155 additions and 306 deletions
7
frontend/components.d.ts
vendored
7
frontend/components.d.ts
vendored
|
@ -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']
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
77
frontend/src/components/StdDataDisplay/StdBatchEdit.vue
Normal file
77
frontend/src/components/StdDataDisplay/StdBatchEdit.vue
Normal 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>
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import dayjs from 'dayjs'
|
||||
|
||||
export interface customRender {
|
||||
value: any
|
||||
text: any
|
||||
record: any
|
||||
index: any
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
146
frontend/src/components/StdDataEntry/components/StdSelector.vue
Normal file
146
frontend/src/components/StdDataEntry/components/StdSelector.vue
Normal 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>
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue