This commit is contained in:
Viet An
2026-07-03 10:22:08 +07:00
parent 4404c4654b
commit b06907097c
11 changed files with 107 additions and 111 deletions

View File

@@ -27,7 +27,7 @@
padding: 0.4rem 0.6rem; padding: 0.4rem 0.6rem;
font-size: 13px; font-size: 13px;
pointer-events: none; pointer-events: none;
box-shadow: 2px 2px 1px rgba(0, 0, 0, 0.1); box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
} }
.control:hover .tooltiptext { .control:hover .tooltiptext {

View File

@@ -334,7 +334,7 @@ function addNew() {
} }
function dataevent(v) { function dataevent(v) {
console.log("SearchBox received dataevent:", v); // Debug log // console.log("SearchBox received dataevent:", v); // Debug log
if (!v || !v.id) { if (!v || !v.id) {
console.error("Invalid data received in SearchBox:", v); console.error("Invalid data received in SearchBox:", v);
@@ -346,11 +346,11 @@ function dataevent(v) {
if (idx < 0) { if (idx < 0) {
// Nếu chưa có trong danh sách, thêm vào đầu // Nếu chưa có trong danh sách, thêm vào đầu
suggestions.value.unshift(v); suggestions.value.unshift(v);
console.log("Added new item to data:", v); // console.log("Added new item to data:", v);
} else { } else {
// Nếu đã có, cập nhật // Nếu đã có, cập nhật
suggestions.value[idx] = v; suggestions.value[idx] = v;
console.log("Updated existing item in data:", v); // console.log("Updated existing item in data:", v);
} }
// Cập nhật orgdata nếu có // Cập nhật orgdata nếu có

View File

@@ -542,7 +542,7 @@ function fieldList() {
component: "datatable/TableOption", component: "datatable/TableOption",
vbind: { pagename: props.pagename }, vbind: { pagename: props.pagename },
title: "Danh sách cột", title: "Danh sách cột",
width: "75%", width: "600px",
height: "630px", height: "630px",
}; };
} }

View File

@@ -592,8 +592,8 @@ th a {
--bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter); --bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter);
background-color: var(--bulma-table-row-hover-background-color); background-color: var(--bulma-table-row-hover-background-color);
} }
.header:hover a { .header:hover {
opacity: 0.75; background-color: hsla(var(--bulma-primary-h), var(--bulma-primary-s), 91%, 1) !important;
} }
.table tbody tr:hover .emptyData { .table tbody tr:hover .emptyData {
background-color: var(--bulma-table-cell-background-color); background-color: var(--bulma-table-cell-background-color);

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div :id="id">
<template v-if="keys.length > 0"> <template v-if="keys.length > 0">
<div <div
class="field is-horizontal" class="field is-horizontal"
@@ -35,7 +35,7 @@
> >
<span class="icon"> <span class="icon">
<Icon <Icon
name="material-symbols:app-registration-rounded" name="material-symbols:data-object-rounded"
:size="20" :size="20"
/> />
</span> </span>
@@ -58,6 +58,11 @@
</div> </div>
</div> </div>
</template> </template>
<Teleport
defer
:to="`.modal-card:has(#${id}) .modal-card-foot`"
>
<div class="buttons is-justify-content-space-between w-full">
<button <button
class="button is-success is-light" class="button is-success is-light"
@click="addAttr()" @click="addAttr()"
@@ -70,7 +75,6 @@
</span> </span>
<span>Thêm thuộc tính</span> <span>Thêm thuộc tính</span>
</button> </button>
<div class="mt-2">
<button <button
class="button is-primary" class="button is-primary"
@click="update()" @click="update()"
@@ -78,17 +82,22 @@
Cập nhật Cập nhật
</button> </button>
</div> </div>
</Teleport>
<Modal <Modal
v-if="comp"
v-bind="{ component: comp, width: '40%', height: '400px', vbind }"
@close="comp = undefined" @close="comp = undefined"
@update="doUpdate" @update="doUpdate"
v-bind="{ component: comp, width: '40%', height: '300px', vbind: vbind }" />
v-if="comp"
></Modal>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: ["field", "close"], props: ["field", "close"],
setup() {
const id = useId();
return { id };
},
data() { data() {
return { return {
keys: [], keys: [],

View File

@@ -23,7 +23,6 @@
<div class="control is-expanded is-flex is-gap-0.5 fs-14"> <div class="control is-expanded is-flex is-gap-0.5 fs-14">
<button <button
:class="['button', radioSave === 'overwrite' ? 'is-primary is-light' : 'is-white']" :class="['button', radioSave === 'overwrite' ? 'is-primary is-light' : 'is-white']"
v-if="isOverwrite()"
@click="changeType('overwrite')" @click="changeType('overwrite')"
> >
<span class="icon"> <span class="icon">
@@ -33,7 +32,7 @@
? 'material-symbols:radio-button-checked-outline' ? 'material-symbols:radio-button-checked-outline'
: 'material-symbols:radio-button-unchecked' : 'material-symbols:radio-button-unchecked'
" "
:size="22" :size="18"
/> />
</span> </span>
<span>Ghi đè</span> <span>Ghi đè</span>
@@ -49,7 +48,7 @@
? 'material-symbols:radio-button-checked-outline' ? 'material-symbols:radio-button-checked-outline'
: 'material-symbols:radio-button-unchecked' : 'material-symbols:radio-button-unchecked'
" "
:size="22" :size="18"
/> />
</span> </span>
<span>Tạo mới</span> <span>Tạo mới</span>
@@ -88,15 +87,13 @@
</p> </p>
</div> </div>
</template> </template>
<div class="field mt-5 px-0 mx-0"> <div class="buttons is-justify-content-flex-end">
<label <button
class="label fs-14" @click="emit('close')"
v-if="status !== undefined" class="button is-white"
:class="status ? 'has-text-primary' : 'has-text-danger'"
> >
{{ status ? "Lưu thiết lập thành công." : "Lỗi. Lưu thiết lập thất bại." }} Huỷ
</label> </button>
<p class="control is-expanded">
<button <button
ref="saveBtn" ref="saveBtn"
:class="['button is-primary', isLoading && 'is-loading']" :class="['button is-primary', isLoading && 'is-loading']"
@@ -110,14 +107,9 @@
</span> </span>
<span>Lưu lại</span> <span>Lưu lại</span>
</button> </button>
</p>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from "vue";
const emit = defineEmits(["modalevent", "close"]);
const store = useStore();
const props = defineProps({ const props = defineProps({
pagename: String, pagename: String,
classify: String, classify: String,
@@ -125,8 +117,9 @@ const props = defineProps({
data: Object, data: Object,
focus: Boolean, focus: Boolean,
}); });
const emit = defineEmits(["modalevent", "close"]);
const store = useStore();
const { $empty, $copy, $patchapi, $insertapi, $findIndex, $snackbar } = useNuxtApp(); const { $empty, $copy, $patchapi, $insertapi, $findIndex, $snackbar } = useNuxtApp();
const radioOption = ref();
const login = { id: 1 }; const login = { id: 1 };
const errors = ref([]); const errors = ref([]);
const radioType = ref(); const radioType = ref();
@@ -134,20 +127,28 @@ const radioDefault = 0;
const radioSave = ref("overwrite"); const radioSave = ref("overwrite");
const name = ref(); const name = ref();
const note = ref(); const note = ref();
const status = undefined; let currentsetting = undefined;
var currentsetting = undefined; const pagedata = store[props.pagename];
var pagename = props.pagename;
var pagedata = store[props.pagename];
const isLoading = ref(false); const isLoading = ref(false);
const saveBtnRef = useTemplateRef("saveBtn"); const saveBtnRef = useTemplateRef("saveBtn");
onMounted(() => { onMounted(() => {
saveBtnRef.value.focus(); saveBtnRef.value.focus();
}); });
function initData() {
radioType.value = store.settingtype.find((v) => v.code === "private");
if (props.pagename) currentsetting = $copy(pagedata.setting || undefined);
// disable temp: for now, radioSave is always 'overwrite'
/* if (!currentsetting) radioSave.value = "new";
else if (currentsetting.user !== login.id) radioSave.value = "new";
else radioSave.value = "overwrite"; */
}
initData();
async function saveSetting() { async function saveSetting() {
errors.value = []; errors.value = [];
let detail = pagename ? { fields: pagedata.fields } : {}; let detail = props.pagename ? { fields: pagedata.fields } : {};
if (pagename) { if (props.pagename) {
let element = pagedata.tablesetting || {}; let element = pagedata.tablesetting || {};
if (element !== store.originsetting) detail.tablesetting = element; if (element !== store.originsetting) detail.tablesetting = element;
if (pagedata.filters ? pagedata.filters.length > 0 : false) { if (pagedata.filters ? pagedata.filters.length > 0 : false) {
@@ -202,22 +203,8 @@ async function saveSetting() {
emit("close"); emit("close");
} }
} }
function isOverwrite() {
return true;
}
function changeType(value) { function changeType(value) {
radioSave.value = value; radioSave.value = value;
} }
function changeOption(v) {
radioOption.value = v.code;
}
function initData() {
radioType.value = store.settingtype.find((v) => v.code === "private");
if (props.pagename) currentsetting = $copy(pagedata.setting ? pagedata.setting : undefined);
// disable temp: for now, radioSave is always 'overwrite'
/* if (!currentsetting) radioSave.value = "new";
else if (currentsetting.user !== login.id) radioSave.value = "new";
else radioSave.value = "overwrite"; */
}
initData();
</script> </script>

View File

@@ -3,8 +3,7 @@
<thead> <thead>
<tr class="fs-14"> <tr class="fs-14">
<th class="is-narrow">#</th> <th class="is-narrow">#</th>
<th>Tên trường</th> <th>Tên</th>
<th>Tên cột</th>
<th class="is-narrow has-text-right">Thao tác</th> <th class="is-narrow has-text-right">Thao tác</th>
</tr> </tr>
</thead> </thead>
@@ -13,16 +12,12 @@
class="fs-14" class="fs-14"
v-for="(v, i) in fields" v-for="(v, i) in fields"
> >
<td style="vertical-align: middle">{{ i }}</td> <td>{{ i }}</td>
<td style="vertical-align: middle">
<a
class="has-text-primary"
@click="openField(v, i)"
>{{ v.name }}</a
>
</td>
<td style="vertical-align: middle">{{ $stripHtml(v.label, 50) }}</td>
<td> <td>
<p>{{ $stripHtml(v.label, 50) }}</p>
<a @click="openField(v, i)">{{ v.name }}</a>
</td>
<td style="vertical-align: middle">
<div class="field has-addons"> <div class="field has-addons">
<p class="control"> <p class="control">
<button <button
@@ -69,12 +64,12 @@
</tbody> </tbody>
</table> </table>
<Modal <Modal
v-if="showmodal"
v-bind="showmodal"
@close="showmodal = undefined" @close="showmodal = undefined"
@update="update" @update="update"
@confirm="remove" @confirm="remove"
v-bind="showmodal" />
v-if="showmodal"
></Modal>
</template> </template>
<script setup> <script setup>
import { cloneDeep } from "es-toolkit"; import { cloneDeep } from "es-toolkit";
@@ -94,7 +89,7 @@ function openField(v, i) {
component: "datatable/FieldAttribute", component: "datatable/FieldAttribute",
title: `${v.name} / ${$stripHtml(v.label)}`, title: `${v.name} / ${$stripHtml(v.label)}`,
width: "50%", width: "50%",
height: "400px", height: "500px",
vbind: { field: v }, vbind: { field: v },
}; };
} }
@@ -124,13 +119,13 @@ function moveUp(v, i) {
updateField(); updateField();
} }
function askConfirm(v, i) { function askConfirm(v, i) {
current = { v: v, i: i }; current = { v, i };
showmodal.value = { showmodal.value = {
component: `dialog/Confirm`, component: "dialog/Confirm",
vbind: { content: "Bạn có muốn xóa cột này không?", duration: 10 }, vbind: { content: `Bạn có muốn xóa cột <span class="tag">${v.name}</span> không?` },
title: "Xóa cột", title: "Xóa cột",
width: "500px", width: "500px",
height: "100px", height: "auto",
}; };
} }
function remove() { function remove() {

View File

@@ -6,6 +6,8 @@ const props = defineProps({
<template> <template>
<a <a
class="tag fs-12 is-rounded is-primary is-light"
style="border: 1px solid var(--bulma-primary-90)"
@click=" @click="
$emit('clickevent', { $emit('clickevent', {
name: 'dataevent', name: 'dataevent',
@@ -14,17 +16,12 @@ const props = defineProps({
component: 'imports/IMEIs', component: 'imports/IMEIs',
title: 'IMEIs', title: 'IMEIs',
width: '80%', width: '80%',
vbind: { variant: props.variant }, vbind: { variant },
}, },
}, },
}) })
" "
> >
<span class="icon"> {{ variant.imei_count }}
<Icon
name="mdi:launch"
:size="18"
/>
</span>
</a> </a>
</template> </template>

View File

@@ -11,6 +11,10 @@ import DataView from "~/components/datatable/DataView.vue";
params: { params: {
values: values:
'id,code,product,product__name,product__os__name,product__external_storage__max_capacity,color,color__code,color__name,color__hex_code,ram,ram__code,ram__capacity,internal_storage,internal_storage__code,internal_storage__capacity,image,image__path,price,note,create_time,update_time', 'id,code,product,product__name,product__os__name,product__external_storage__max_capacity,color,color__code,color__name,color__hex_code,ram,ram__code,ram__capacity,internal_storage,internal_storage__code,internal_storage__capacity,image,image__path,price,note,create_time,update_time',
distinct_values: {
imei_count: { type: 'Count', field: 'imeis' },
},
summary: 'annotate',
sort: 'id', sort: 'id',
}, },
timeopt: { time: 36000 }, timeopt: { time: 36000 },

View File

@@ -14,7 +14,7 @@
ref="file-input" ref="file-input"
type="file" type="file"
:id="$id()" :id="$id()"
:multiple="multiple ?? true" :multiple="multiple"
name="resume" name="resume"
@change="doChange" @change="doChange"
/> />
@@ -48,7 +48,10 @@ const props = defineProps({
type: Array, type: Array,
convert: String, convert: String,
quality: Number, quality: Number,
multiple: Boolean, multiple: {
type: Boolean,
default: true,
},
}); });
const emit = defineEmits(["files"]); const emit = defineEmits(["files"]);

View File

@@ -89,7 +89,8 @@ export default defineNuxtPlugin((nuxtApp) => {
let rs; let rs;
if (Array.isArray(data)) { if (Array.isArray(data)) {
rs = await $fetch(`${curpath}import-data/${api.url.substring(5, api.url.length - 1)}/`, { const model = api.url.slice(5);
rs = await $fetch(`${curpath}import-data/${model}`, {
method: "POST", method: "POST",
body: data, body: data,
params: { values, action: "import" }, params: { values, action: "import" },