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;
font-size: 13px;
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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,10 @@ import DataView from "~/components/datatable/DataView.vue";
params: {
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',
distinct_values: {
imei_count: { type: 'Count', field: 'imeis' },
},
summary: 'annotate',
sort: 'id',
},
timeopt: { time: 36000 },

View File

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

View File

@@ -89,7 +89,8 @@ export default defineNuxtPlugin((nuxtApp) => {
let rs;
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",
body: data,
params: { values, action: "import" },