This commit is contained in:
Viet An
2026-05-15 11:18:33 +07:00
parent 869138c003
commit 0ef1d29850
18 changed files with 175 additions and 111 deletions

View File

@@ -2,7 +2,7 @@
<div> <div>
<div <div
class="field has-addons" class="field has-addons"
:id="docid" :id="$id()"
> >
<div class="control has-icons-left has-icons-right is-expanded"> <div class="control has-icons-left has-icons-right is-expanded">
<div <div
@@ -17,7 +17,6 @@
:class="[ :class="[
'input', 'input',
{ {
'is-danger': error,
'has-text-dark': disabled, 'has-text-dark': disabled,
}, },
]" ]"
@@ -171,11 +170,9 @@ const selected = ref();
const showmodal = ref(); const showmodal = ref();
const params = ref(props.api && $findapi(props.api).params); const params = ref(props.api && $findapi(props.api).params);
const orgdata = ref(); const orgdata = ref();
const error = ref(false);
const focused = ref(false); const focused = ref(false);
const count1 = ref(0); const count1 = ref(0);
const count2 = ref(0); const count2 = ref(0);
const docid = ref($id());
const pos = ref(); const pos = ref();
getPos(); getPos();

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="field has-addons is-justify-content-center"> <div class="field has-addons is-justify-content-center">
<p class="control"> <!-- <p class="control">
<button <button
class="button is-light is-primary" class="button is-light is-primary"
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'az' })" @click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'az' })"
@@ -37,7 +37,7 @@
style="top: 110%; bottom: unset; min-width: max-content; left: 25px" style="top: 110%; bottom: unset; min-width: max-content; left: 25px"
>Sắp xếp giảm dần</span >Sắp xếp giảm dần</span
> >
</p> </p> -->
<p class="control"> <p class="control">
<button <button
class="button is-light is-primary" class="button is-light is-primary"

View File

@@ -103,7 +103,6 @@
:row="v" :row="v"
v-if="field.template" v-if="field.template"
@clickevent="clickEvent($event, v, field)" @clickevent="clickEvent($event, v, field)"
@dynamicCompEvent="onDynamicCompEvent"
/> />
<span v-else>{{ v[field.name] }}</span> <span v-else>{{ v[field.name] }}</span>
</td> </td>
@@ -671,13 +670,6 @@ const doubleClick = function (field, v) {
}; };
const tableStyle = getSettingStyle("table"); const tableStyle = getSettingStyle("table");
setTimeout(() => updateShow(), 200); setTimeout(() => updateShow(), 200);
async function onDynamicCompEvent(e) {
console.log("DataTable received dynamicCompEvent", e);
if (e.type === "refresh") {
updateShow(); // doesn't get new data
}
}
</script> </script>
<style scoped> <style scoped>
:deep(.table tbody tr:hover td, .table tbody tr:hover th) { :deep(.table tbody tr:hover td, .table tbody tr:hover th) {

View File

@@ -151,12 +151,12 @@ const checkDataChanges = async () => {
delete conn1.params.sort; delete conn1.params.sort;
delete conn1.params.values; delete conn1.params.values;
conn1.params.summary = "aggregate"; // conn1.params.summary = "aggregate";
conn1.params.distinct_values = JSON.stringify({ // conn1.params.distinct_values = JSON.stringify({
total_count: { type: "Count", field: "id" }, // total_count: { type: "Count", field: "id" },
last_updated: { type: "Max", field: "update_time" }, // last_updated: { type: "Max", field: "update_time" },
last_created: { type: "Max", field: "create_time" }, // last_created: { type: "Max", field: "create_time" },
}); // });
connlist.push(conn1); connlist.push(conn1);
@@ -263,6 +263,8 @@ const refreshData = async () => {
startAutoCheck(); startAutoCheck();
}; };
provide("refreshData", refreshData);
watch( watch(
() => props.realtime, () => props.realtime,
(newVal) => { (newVal) => {

View File

@@ -1,11 +1,19 @@
<template> <template>
<div> <div>
<div <div
v-for="(v, i) in arr" v-for="(v, i) in lines"
:key="i" :key="i"
:class="i > 0 && 'mt-4'" :class="i > 0 && 'mt-4'"
> >
<p class="font-semibold">Dòng thứ {{ i + 1 }}<span class="has-text-danger"> *</span></p> <p class="font-semibold">
Dòng thứ {{ i + 1 }}
<span
v-if="i === 0"
class="has-text-danger"
>
*
</span>
</p>
<div class="field has-addons mt-1"> <div class="field has-addons mt-1">
<div class="control is-expanded"> <div class="control is-expanded">
<input <input
@@ -14,19 +22,6 @@
v-model="v.label" v-model="v.label"
/> />
</div> </div>
<div class="control">
<button
class="button is-success is-light"
@click="add()"
>
<span class="icon">
<Icon
name="material-symbols:add-rounded"
:size="20"
/>
</span>
</button>
</div>
<div <div
class="control" class="control"
@click="remove(i)" @click="remove(i)"
@@ -41,6 +36,19 @@
</span> </span>
</button> </button>
</div> </div>
<div class="control">
<button
class="button is-success is-light"
@click="add"
>
<span class="icon">
<Icon
name="material-symbols:add-rounded"
:size="20"
/>
</span>
</button>
</div>
</div> </div>
<p <p
class="help has-text-danger" class="help has-text-danger"
@@ -52,7 +60,7 @@
<div class="buttons mt-5"> <div class="buttons mt-5">
<button <button
class="button is-primary has-text-white" class="button is-primary has-text-white"
@click="update()" @click="update"
> >
Cập nhật Cập nhật
</button> </button>
@@ -70,7 +78,7 @@ export default {
props: ["label"], props: ["label"],
data() { data() {
return { return {
arr: [], lines: [],
}; };
}, },
created() { created() {
@@ -79,37 +87,37 @@ export default {
if (!this.$empty(v)) { if (!this.$empty(v)) {
let label = v + "</p>"; let label = v + "</p>";
label = this.$stripHtml(label); label = this.$stripHtml(label);
this.arr.push({ label }); this.lines.push({ label });
} }
}); });
}, },
methods: { methods: {
add() { add() {
this.arr.push({ label: undefined }); this.lines.push({ label: undefined });
}, },
remove(i) { remove(i) {
this.$remove(this.arr, i); this.$remove(this.lines, i);
}, },
checkError() { checkError() {
let error = false; let error = false;
this.arr.map((v) => { this.lines.map((v) => {
if (this.$empty(v.label)) { if (this.$empty(v.label)) {
v.error = "Nội dung không được bỏ trống"; v.error = "Nội dung không được bỏ trống";
error = true; error = true;
} }
}); });
if (error) this.arr = this.$copy(this.arr); if (error) this.lines = this.$copy(this.lines);
return error; return error;
}, },
update() { update() {
if (this.checkError()) return; if (this.checkError()) return;
let label = ""; let label = "";
if (this.arr.length > 1) { if (this.lines.length > 1) {
this.arr.map((v, i) => { this.lines.map((v, i) => {
label += `<p${i < this.arr.length - 1 ? ' style="border-bottom: 1px solid white;"' : ""}>${v.label.trim()}</p>`; label += `<p${i < this.lines.length - 1 ? ' style="border-bottom: 1px solid white;"' : ""}>${v.label.trim()}</p>`;
}); });
label = `<div>${label}</div>`; label = `<div>${label}</div>`;
} else label = this.arr[0].label.trim(); } else label = this.lines[0].label.trim();
this.$emit("modalevent", { name: "label", data: label }); this.$emit("modalevent", { name: "label", data: label });
this.$emit("close"); this.$emit("close");
}, },

View File

@@ -9,7 +9,7 @@
</p> </p>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Chọn chế độ lưu <span class="has-text-danger"> * </span></label> <label class="label">Chọn chế độ lưu</label>
<div class="control is-expanded fs-14"> <div class="control is-expanded fs-14">
<button <button
class="button is-white has-text-inherit" class="button is-white has-text-inherit"
@@ -54,7 +54,7 @@
<input <input
class="input" class="input"
type="text" type="text"
placeholder="" placeholder="products, variants-table, etc."
v-model="name" v-model="name"
v-on:keyup.enter="saveSetting" v-on:keyup.enter="saveSetting"
/> />
@@ -67,28 +67,16 @@
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label"> tả </label> <label class="label"> tả</label>
<p class="control is-expanded"> <p class="control is-expanded">
<textarea <textarea
class="textarea" class="textarea"
rows="3" rows="1"
placeholder="Note"
v-model="note" v-model="note"
></textarea> ></textarea>
</p> </p>
</div> </div>
<!--
<div class="field mt-4 px-0 mx-0">
<label class="label fs-14">Loại thiết lập <span class="has-text-danger"> * </span>
</label>
<div class="control is-expanded fs-14">
<span class="mr-4" v-for="(v,i) in $filter(store.settingtype, {code: ['private', 'public']})">
<a class="icon-text" @click="changeOption(v)">
<SvgIcon v-bind="{name: `radio-${radioOption===v.code? '' : 'un'}checked.svg`, type: radioOption===v.code? 'primary' : 'gray', size: 22}"></SvgIcon>
</a>
{{v.name}}
</span>
</div>
</div>-->
</template> </template>
<div class="field mt-5 px-0 mx-0"> <div class="field mt-5 px-0 mx-0">
<label <label
@@ -100,6 +88,7 @@
</label> </label>
<p class="control is-expanded"> <p class="control is-expanded">
<button <button
ref="saveBtn"
:class="['button is-primary', isLoading && 'is-loading']" :class="['button is-primary', isLoading && 'is-loading']"
@click="saveSetting()" @click="saveSetting()"
> >
@@ -134,6 +123,10 @@ var currentsetting = undefined;
var pagename = props.pagename; var pagename = props.pagename;
var pagedata = store[props.pagename]; var pagedata = store[props.pagename];
const isLoading = ref(false); const isLoading = ref(false);
const saveBtnRef = useTemplateRef("saveBtn");
onMounted(() => {
saveBtnRef.value.focus();
});
async function saveSetting() { async function saveSetting() {
errors.value = []; errors.value = [];
@@ -184,7 +177,7 @@ async function saveSetting() {
copy[idx] = result; copy[idx] = result;
store.commit("settings", copy); store.commit("settings", copy);
} }
$snackbar("Lưu thiết lập thành công"); $snackbar("Lưu thiết lập thành công", undefined, "Success");
emit("modalevent", { name: "updatesetting", data: result }); emit("modalevent", { name: "updatesetting", data: result });
emit("close"); emit("close");
} }

View File

@@ -2,7 +2,7 @@
<div class="tabs is-boxed"> <div class="tabs is-boxed">
<ul> <ul>
<li <li
v-for="v in fieldType" v-for="v in fieldTypes"
:class="selectType.code === v.code && 'is-active'" :class="selectType.code === v.code && 'is-active'"
> >
<a <a
@@ -273,16 +273,16 @@ var data = [];
var current = 1; var current = 1;
var filterData = []; var filterData = [];
var loading = false; var loading = false;
const fieldType = [ const fieldTypes = [
{ code: "formula", name: "Tạo công thức" },
{ code: "empty", name: "Tạo cột rỗng" }, { code: "empty", name: "Tạo cột rỗng" },
{ code: "formula", name: "Tạo công thức" },
]; ];
var tags = []; var tags = [];
var formula = undefined; var formula = undefined;
var name = `f${$id().toLocaleLowerCase()}`; var name = `f${$id().toLocaleLowerCase()}`;
var label = undefined; var label = undefined;
const errors = ref([]); const errors = ref([]);
var selectType = fieldType.find((v) => v.code === "empty"); var selectType = fieldTypes.find((v) => v.code === "empty");
var radioType = ref(datatype.find((v) => v.code === "string")); var radioType = ref(datatype.find((v) => v.code === "string"));
var fields = []; var fields = [];
var options = undefined; var options = undefined;
@@ -293,7 +293,7 @@ var choices = [
{ code: "function", name: "Dùng hàm số" }, { code: "function", name: "Dùng hàm số" },
]; ];
const choice = ref("column"); const choice = ref("column");
var funcs = [ const funcs = [
{ code: "sum", name: "Sum" }, { code: "sum", name: "Sum" },
{ code: "max", name: "Max" }, { code: "max", name: "Max" },
{ code: "min", name: "Min" }, { code: "min", name: "Min" },
@@ -436,7 +436,6 @@ function createField() {
emit("close"); emit("close");
} }
function createEmptyField() { function createEmptyField() {
console.log("createEmptyField");
errors.value = []; errors.value = [];
if (!$empty(name) ? $empty(name.trim()) : true) if (!$empty(name) ? $empty(name.trim()) : true)
errors.value.push({ name: "name", message: "Tên không được bỏ trống." }); errors.value.push({ name: "name", message: "Tên không được bỏ trống." });
@@ -454,7 +453,6 @@ function createEmptyField() {
message: "Mô tả bị trùng. Hãy đặt mô tả khác.", message: "Mô tả bị trùng. Hãy đặt mô tả khác.",
}); });
} }
console.log("errors", errors);
if (errors.value.length > 0) return; if (errors.value.length > 0) return;
let field = $createField(name.trim(), label.trim(), radioType.value.code, true); let field = $createField(name.trim(), label.trim(), radioType.value.code, true);
if (selectType.code === "chart") field = createChartField(); if (selectType.code === "chart") field = createChartField();
@@ -463,7 +461,6 @@ function createEmptyField() {
copy.update = { fields: copy.fields }; copy.update = { fields: copy.fields };
store.commit(props.pagename, copy); store.commit(props.pagename, copy);
//pagedata = copy //pagedata = copy
console.log("field", field);
emit("newfield", field); emit("newfield", field);
label = undefined; label = undefined;
name = `f${$id()}`; name = `f${$id()}`;

View File

@@ -59,7 +59,7 @@
:placeholder="lang === 'vi' ? 'Nhập từ khóa...' : 'Enter keyword...'" :placeholder="lang === 'vi' ? 'Nhập từ khóa...' : 'Enter keyword...'"
class="input is-orange fs-12" class="input is-orange fs-12"
:style="{ :style="{
maxWidth: '180px', maxWidth: '150px',
width: this.store.viewport === 1 ? '150px' : 'auto', width: this.store.viewport === 1 ? '150px' : 'auto',
}" }"
/> />

View File

@@ -1,10 +0,0 @@
<template>
<span :style="color ? `color:${color}` : ''">{{ $numtoString(value) }}</span>
</template>
<script setup>
const { $numtoString } = useNuxtApp();
const props = defineProps({
value: Number,
color: String,
});
</script>

View File

@@ -24,9 +24,13 @@ function selected(field, data) {
else body.value[field] = data.id; else body.value[field] = data.id;
} }
const refreshData = inject("refreshData");
async function createProduct() { async function createProduct() {
isPending.value = true; isPending.value = true;
const res = await $insertapi("product", body.value); const res = await $insertapi("product", body.value);
if (res !== "error") {
if (refreshData) refreshData();
}
isPending.value = false; isPending.value = false;
} }
</script> </script>

View File

@@ -0,0 +1,49 @@
<script setup>
import Modal from "@/components/Modal.vue";
const props = defineProps({
product: Object,
});
const { $deleteapi } = useNuxtApp();
const showConfirmModal = ref(null);
function displayModal() {
showConfirmModal.value = {
component: "dialog/Confirm",
title: "Xoá sản phẩm",
width: "500px",
height: "auto",
vbind: {
content: `Bạn xác nhận xoá sản phẩm <b>${props.product.name}</b>?`,
onModalevent: deleteProduct,
},
};
}
const refreshData = inject("refreshData");
async function deleteProduct() {
const res = await $deleteapi("product", props.product.id);
if (res !== "error") {
if (refreshData) refreshData();
}
}
</script>
<template>
<a
class="has-text-danger"
@click="displayModal"
>
<span class="icon">
<Icon
name="material-symbols:delete-outline-rounded"
:size="18"
/>
</span>
<Modal
v-bind="showConfirmModal"
@close="showConfirmModal = null"
/>
</a>
</template>

View File

@@ -5,7 +5,6 @@ const props = defineProps({
variant: Object, variant: Object,
}); });
const emit = defineEmits(["dynamicCompEvent"]);
const { $deleteapi } = useNuxtApp(); const { $deleteapi } = useNuxtApp();
const showConfirmModal = ref(null); const showConfirmModal = ref(null);
@@ -22,11 +21,11 @@ function displayModal() {
}; };
} }
const refreshData = inject("refreshData");
async function deleteVariant() { async function deleteVariant() {
const res = await $deleteapi("Product_Variant", props.variant.id); const res = await $deleteapi("Product_Variant", props.variant.id);
if (res !== "error") { if (res !== "error") {
// emit to parent, which is DataTable if (refreshData) refreshData();
emit("dynamicCompEvent", { type: "refresh" });
} }
} }
</script> </script>

View File

@@ -1,22 +1,22 @@
<script setup> <script setup>
import AddProductForm from "@/components/imports/AddProductForm.vue";
import AddProductVariant from "@/components/imports/AddProductVariant.vue"; import AddProductVariant from "@/components/imports/AddProductVariant.vue";
import Products from "@/components/imports/Products.vue";
const menus = [ const menus = [
{ {
id: "add-product", id: "product",
name: "Tạo sản phẩm", name: "Sản phẩm",
}, },
{ {
id: "add-product-variant", id: "product-variant",
name: "Thêm phiên bản", name: "Phiên bản",
}, },
]; ];
const activeMenu = ref(menus[1]); const activeMenu = ref(menus[0]);
</script> </script>
<template> <template>
<div class="fixed-grid has-12-cols"> <div class="fixed-grid has-12-cols">
<div class="grid is-gap-4"> <div class="grid is-gap-4">
<div class="cell is-col-span-3"> <div class="cell is-col-span-2">
<aside class="menu"> <aside class="menu">
<ul class="menu-list"> <ul class="menu-list">
<li <li
@@ -25,27 +25,23 @@ const activeMenu = ref(menus[1]);
> >
<a <a
@click="activeMenu = menu" @click="activeMenu = menu"
:class="[ :class="[{ 'is-active': activeMenu.id === menu.id }]"
'fs-13',
{
'is-active': activeMenu.id === menu.id,
},
]"
>{{ menu.name }}</a >{{ menu.name }}</a
> >
</li> </li>
</ul> </ul>
</aside> </aside>
</div> </div>
<div class="cell is-col-span-9"> <div class="cell is-col-span-10">
<AddProductForm v-if="activeMenu.id === 'add-product'" /> <Products v-if="activeMenu.id === 'product'" />
<AddProductVariant v-if="activeMenu.id === 'add-product-variant'" /> <AddProductVariant v-if="activeMenu.id === 'product-variant'" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.menu-list a { .menu-list a {
font-size: 0.95em;
--bulma-menu-list-link-padding: 0.75em 1.25em; --bulma-menu-list-link-padding: 0.75em 1.25em;
&:not(.is-active) { &:not(.is-active) {

View File

@@ -0,0 +1,25 @@
<script setup>
import DataView from "@/components/datatable/DataView.vue";
</script>
<template>
<DataView
v-bind="{
api: 'product',
setting: 'products',
pagename: 'products',
params: {
values:
'id,code,name,manufacturer,manufacturer__name,os,os__name,battery,battery__code,screen,cpu,cpu__name,gpu,gpu__name,camera_system,camera_system__code,sim,sim__code,network_technology,network_technology__name,charging_technology,charging_technology__code,external_storage,external_storage__max_capacity,ip_rating,ip_rating__code,design,create_time,update_time',
sort: 'id',
},
timeopt: { time: 36000 },
modal: {
component: 'imports/AddProductForm',
title: 'Tạo sản phẩm',
width: '75%',
height: 'auto',
},
}"
/>
</template>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div <div
class="snackbar is-flex is-align-items-center is-gap-3 pl-3 pr-1.5 py-2 rounded-md has-background-grey-35 has-text-white" class="snackbar is-flex is-align-items-center is-gap-3 pl-3 pr-1.5 py-2 rounded-md has-background-grey-25 has-text-white"
> >
<component <component
:is="resolvedComponent" :is="resolvedComponent"

View File

@@ -4,8 +4,13 @@ export default defineNuxtPlugin(() => {
//==========Find & filter================= //==========Find & filter=================
const find = function (arr, obj, attr) { const find = function (arr, obj, attr) {
if (typeof arr === "object" && !Array.isArray(arr)) { if (!Array.isArray(arr)) {
arr = Object.values(arr); if (typeof arr === "object") {
arr = Object.values(arr);
} else {
console.error(`arr "${arr}" is not an array`);
return;
}
} }
const keys = Object.keys(obj); const keys = Object.keys(obj);
let found = arr.find((v) => { let found = arr.find((v) => {

View File

@@ -570,7 +570,10 @@ export default defineNuxtPlugin((nuxtApp) => {
commit: "product", commit: "product",
url: "data/Product/", url: "data/Product/",
url_detail: "data-detail/Product/", url_detail: "data-detail/Product/",
params: {}, params: {
values:
"id,code,name,manufacturer,manufacturer__name,os,os__name,battery,battery__code,screen,cpu,cpu__name,gpu,gpu__name,camera_system,camera_system__code,sim,sim__code,network_technology,network_technology__name,charging_technology,charging_technology__code,external_storage,external_storage__max_capacity,ip_rating,ip_rating__code,design,create_time,update_time",
},
}, },
{ {
name: "Product_Variant", name: "Product_Variant",
@@ -1188,7 +1191,7 @@ export default defineNuxtPlugin((nuxtApp) => {
// insert data // insert data
const insertapi = async function (name, data, values, notify) { const insertapi = async function (name, data, values, notify) {
try { try {
let found = findapi(name); const found = findapi(name);
const curpath = found.path ? paths.find((x) => x.name === found.path).url : path; const curpath = found.path ? paths.find((x) => x.name === found.path).url : path;
let rs; let rs;
if (!Array.isArray(data)) { if (!Array.isArray(data)) {
@@ -1464,13 +1467,13 @@ export default defineNuxtPlugin((nuxtApp) => {
}); });
} }
if (found.commit) { if (found.commit) {
let copy = JSON.parse(JSON.stringify($store[found.commit])); const copy = $copy($store[found.commit]);
if (!Array.isArray(id)) { if (!Array.isArray(id)) {
let index = copy.findIndex((v) => v.id === id); const index = copy.findIndex((v) => v.id === id);
if (index >= 0) $remove(copy, index); if (index >= 0) $remove(copy, index);
} else { } else {
rs.forEach((element) => { rs.forEach((element) => {
let index = copy.findIndex((v) => v.id === element.id); const index = copy.findIndex((v) => v.id === element.id);
if (index >= 0) $remove(copy, index); if (index >= 0) $remove(copy, index);
}); });
} }
@@ -1478,7 +1481,7 @@ export default defineNuxtPlugin((nuxtApp) => {
} }
return id; return id;
} catch (err) { } catch (err) {
console.log(err); console.error(err);
if (err.response) { if (err.response) {
let content = `<span>Đã xảy ra lỗi, xóa dữ liệu không thành công</span>`; let content = `<span>Đã xảy ra lỗi, xóa dữ liệu không thành công</span>`;
if (err.response.data) if (err.response.data)

View File

@@ -7,6 +7,8 @@ import POS from "@/components/pos/POS.vue";
import CreateReceipts from "@/components/receipts/CreateReceipts.vue"; import CreateReceipts from "@/components/receipts/CreateReceipts.vue";
import Return from "@/components/receipts/Return.vue"; import Return from "@/components/receipts/Return.vue";
import Imports from "@/components/imports/Imports.vue"; import Imports from "@/components/imports/Imports.vue";
import AddProductForm from "@/components/imports/AddProductForm.vue";
import DeleteProduct from "@/components/imports/DeleteProduct.vue";
import AddOS from "@/components/imports/addons/AddOS.vue"; import AddOS from "@/components/imports/addons/AddOS.vue";
import AddManufacturer from "@/components/imports/addons/AddManufacturer.vue"; import AddManufacturer from "@/components/imports/addons/AddManufacturer.vue";
import AddBattery from "@/components/imports/addons/AddBattery.vue"; import AddBattery from "@/components/imports/addons/AddBattery.vue";
@@ -168,6 +170,8 @@ const components = {
CreateReceipts, CreateReceipts,
Return, Return,
Imports, Imports,
AddProductForm,
DeleteProduct,
AddOS, AddOS,
AddManufacturer, AddManufacturer,
AddBattery, AddBattery,