From dc624af9bd8091339cb0e92fdc24296ab8b009d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E6=B0=B8=E9=A1=BA?= <971387674@qq.com> Date: Mon, 5 Feb 2024 14:53:38 +0800 Subject: [PATCH] =?UTF-8?q?feature/2.10.1.2401.01-=E7=A4=BE=E4=BF=9D?= =?UTF-8?q?=E7=A6=8F=E5=88=A9=E6=96=B9=E6=A1=88=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/salaryFilesEditSlide/index.js | 2 +- .../welfarePlanCustomDialog/index.js | 45 +++-- .../components/welfarePlanEditSlide/index.js | 160 ++++++++++++++++++ .../components/welfarePlanList/index.js | 32 +++- .../welfarePlan/config.js | 73 +++++++- .../welfarePlan/index.js | 19 ++- .../welfarePlan/index.less | 123 ++++++++++++++ pc4mobx/hrmSalary/stores/programme.js | 2 + 8 files changed, 419 insertions(+), 37 deletions(-) create mode 100644 pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanEditSlide/index.js diff --git a/pc4mobx/hrmSalary/pages/payrollFiles/components/salaryFilesEditSlide/index.js b/pc4mobx/hrmSalary/pages/payrollFiles/components/salaryFilesEditSlide/index.js index 1edd3e56..d4eaf520 100644 --- a/pc4mobx/hrmSalary/pages/payrollFiles/components/salaryFilesEditSlide/index.js +++ b/pc4mobx/hrmSalary/pages/payrollFiles/components/salaryFilesEditSlide/index.js @@ -212,7 +212,7 @@ class Index extends Component { {...adjLogRecordDialog} onCancel={() => this.setState({ adjLogRecordDialog: { - adjLogRecordDialog, visible: false, title: "", salaryArchiveId: "" + ...adjLogRecordDialog, visible: false, title: "", salaryArchiveId: "" } })} /> diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanCustomDialog/index.js b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanCustomDialog/index.js index 8c9d2c7f..00fba733 100644 --- a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanCustomDialog/index.js +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanCustomDialog/index.js @@ -9,7 +9,7 @@ import { inject, observer } from "mobx-react"; import { WeaDialog, WeaLocaleProvider, WeaTools } from "ecCom"; import { Button, message } from "antd"; import * as API from "../../../../../apis/welfareScheme"; -import { getSearchs } from "../../../../../util"; +import { getConditionDomkeys, getSearchs } from "../../../../../util"; import { customPlanConditons } from "../../config"; const getKey = WeaTools.getKey; @@ -35,37 +35,48 @@ class Index extends Component { if (status) { const { form } = data; this.setState({ - conditions: customPlanConditons + conditions: _.map(customPlanConditons, item => ({ + ...item, items: _.map(item.items, o => { + if (getKey(o) === "welfareType" && props.customId) { + return { ...o, viewAttr: 1 }; + } else { + return { ...o }; + } + }) + })) }, () => { const { programmeStore: { planCustomForm } } = props; planCustomForm.initFormFields(this.state.conditions); - // planCustomForm.updateFields({ [`${selectedKey}_schemeName`]: { value: copyName } }); + _.forEach(getConditionDomkeys(this.state.conditions), k => { + if (k === "paymentScope" && props.customId) { + planCustomForm.updateFields({ [k]: { value: form[k].join(",") } }); + } else { + planCustomForm.updateFields({ [k]: { value: form[k] || "" } }); + } + }); }); } }); }; save = () => { - const { programmeStore: { planCustomForm }, copyId: id, selectedKey } = this.props; + const { programmeStore: { planCustomForm }, customId: id, selectedKey } = this.props; planCustomForm.validateForm().then(f => { if (f.isValid) { this.setState({ loading: true }); const payload = { - ...planCustomForm.getFormParams(), + id, ...planCustomForm.getFormParams(), paymentScope: planCustomForm.getFormParams().paymentScope.split(",") }; - console.log(payload); - return; - API.copyScheme({ id, schemeName: planCustomForm.getFormDatas()[`${selectedKey}_schemeName`].value }) - .then(({ status, errormsg }) => { - this.setState({ loading: false }); - if (status) { - message.success(getLabel(30700, "操作成功!")); - this.props.onCancel(true); - } else { - message.error(errormsg); - } - }); + API[!id ? "createSICategory" : "updateCustomCategory"](payload).then(({ status, errormsg }) => { + this.setState({ loading: false }); + if (status) { + message.success(getLabel(30700, "操作成功!")); + this.props.onCancel(true); + } else { + message.error(errormsg); + } + }); } else { f.showErrors(); } diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanEditSlide/index.js b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanEditSlide/index.js new file mode 100644 index 00000000..5522d116 --- /dev/null +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanEditSlide/index.js @@ -0,0 +1,160 @@ +/* + * Author: 黎永顺 + * name:社保方案页面重构-社保方案编辑与查看 + * Description: + * Date: 2024/2/5 + */ +import React, { Component } from "react"; +import { inject, observer } from "mobx-react"; +import { WeaLocaleProvider, WeaSlideModal, WeaTools } from "ecCom"; +import { Button, Modal } from "antd"; +import * as API from "../../../../../apis/welfareScheme"; +import { getTaxAgentSelectListAsAdmin } from "../../../../../apis/taxAgent"; +import { getConditionDomkeys, getSearchs } from "../../../../../util"; +import { planConditons } from "../../config"; + +const getKey = WeaTools.getKey; +const getLabel = WeaLocaleProvider.getLabel; + +@inject("programmeStore") +@observer +class Index extends Component { + constructor(props) { + super(props); + this.state = { + loading: false, conditions: [] + }; + } + + componentWillReceiveProps(nextProps, nextContext) { + if (nextProps.visible !== this.props.visible && nextProps.visible) { + document.querySelector(".salary-welfare-plan-wrapper").classList.add("zIndex0-welfare-plan"); + const promise = this.getForm(nextProps); + } else if (nextProps.visible !== this.props.visible && !nextProps.visible) { + document.querySelector(".salary-welfare-plan-wrapper").classList.remove("zIndex0-welfare-plan"); + nextProps.programmeStore.initPlanForm(); + } + } + + getForm = async (props) => { + const { data: result } = await getTaxAgentSelectListAsAdmin(); + const { id, welfareTypeEnum, programmeStore: { planForm } } = props; + API.getForm(id ? _.assign({ welfareTypeEnum }, { id }) : { welfareTypeEnum }).then(({ status, data }) => { + if (status) { + console.log(data, result); + const { form: { schemeBatch } } = data; + this.setState({ + conditions: _.map(planConditons, item => ({ + ...item, items: _.map(item.items, o => { + if (getKey(o) === "taxAgentIds") { + return { + ...o, hide: schemeBatch["sharedType"] === "0" || _.isNil(schemeBatch["sharedType"]), + viewAttr: schemeBatch["sharedType"] === "1" ? 3 : 2, + options: _.map(result, k => ({ key: k.id, showname: k.content })) + }; + } + return o; + }) + })) + }, () => { + planForm.initFormFields(this.state.conditions); + _.map(getConditionDomkeys(this.state.conditions), k => { + if (k === "sharedType") { + planForm.updateFields({ [k]: schemeBatch[k] || "0" }); + } else { + planForm.updateFields({ [k]: schemeBatch[k] || "" }); + } + }); + }); + } + }); + }; + + save = async () => { + const { programmeStore: { planForm, setHasBeenModify } } = this.props; + planForm.validateForm().then(f => { + if (f.isValid) { + if (planForm.getFormParams().sharedType === "1" && _.isEmpty(planForm.getFormParams().taxAgentIds)) { + planForm.showError("taxAgentIds", getLabel(111, "\"可见范围\"未填写")); + } else { + console.log(planForm.getFormParams()); + } + } else { + f.showErrors(); + } + }); + }; + renderTitle = () => { + const { loading } = this.state, { title, showOperateBtn } = this.props; + return
+
+
+
{title}
+
+
+ { + showOperateBtn && + + } +
+
; + }; + handleClose = (visible = false) => { + const { programmeStore: { hasBeenModify, setHasBeenModify }, onClose } = this.props; + if (hasBeenModify) { + Modal.confirm({ + title: getLabel(131329, "信息确认"), + content: getLabel(545770, "确定放弃填写吗?放弃后数据将不会被保存!"), + onOk: () => { + onClose(visible); + setHasBeenModify(false); + } + }); + } else { + onClose(visible); + setHasBeenModify(false); + } + }; + onChange = (params) => { + const { programmeStore: { planForm, setHasBeenModify } } = this.props; + const key = _.keys(params)[0]; + if (key === "sharedType") { + this.setState({ + conditions: _.map(this.state.conditions, it => ({ + ...it, items: _.map(it.items, o => { + if (getKey(o) === "taxAgentIds") { + return { + ...o, hide: params[key].value === "0", + rules: params[key].value === "1" ? "required|string" : "", + viewAttr: params[key].value === "1" ? 3 : 2 + }; + } + return { ...o }; + }) + })) + }, () => { + planForm.updateFields({ taxAgentIds: "" }); + }); + } + setHasBeenModify(true); + }; + + render() { + const { conditions } = this.state; + const { + programmeStore: { planForm, setHasBeenModify }, showOperateBtn, visible + } = this.props; + return ( + this.handleClose(false)} + content={
+ {getSearchs(planForm, conditions, 1, false, this.onChange)} +
} + /> + ); + } +} + +export default Index; diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanList/index.js b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanList/index.js index 9571e336..f6af25b1 100644 --- a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanList/index.js +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/components/welfarePlanList/index.js @@ -11,6 +11,7 @@ import { Dropdown, Menu, message, Modal } from "antd"; import TipLabel from "../../../../../components/TipLabel"; import WelfarePlanCopyDialog from "../welfarePlanCopyDialog"; import WelfarePlanCustomDialog from "../welfarePlanCustomDialog"; +import WelfarePlanEditSlide from "../welfarePlanEditSlide"; import { tabWelfarePlanList, welfarePlanCopyConditions, welfarePlanCustomTipList } from "../../config"; import * as API from "../../../../../apis/welfareScheme"; @@ -31,7 +32,8 @@ class Index extends Component { dataSource: [], columns: [], loading: false, pageInfo: { current: 1, pageSize: 10, total: 0 }, copyDialog: { visible: false, title: "", copyId: "", copyName: "", conditions: [] }, - customDialog: { visible: false, title: "", customId: "" } + customDialog: { visible: false, title: "", customId: "" }, + welfarePlanEditSlide: { visible: false, id: "", welfareTypeEnum: "", title: "" } }; } @@ -55,9 +57,9 @@ class Index extends Component { } getList = (props) => { - const { programmeStore: { planSearchForm }, selectedKey: welfareTypeEnum, custom } = props; + const { programmeStore: { planSearchForm }, selectedKey: welfareTypeEnum, customQuery } = props; const { pageInfo } = this.state; - const originPayload = { ...pageInfo, welfareTypeEnum }, customPayload = { welfareTypeEnum: custom }, + const originPayload = { ...pageInfo, welfareTypeEnum }, customPayload = { welfareTypeEnum: customQuery }, welfarePayload = { ...planSearchForm.getFormParams() }; const payload = welfareTypeEnum === "CUSTOM" ? { ...originPayload, ...customPayload } : { ...originPayload, ...welfarePayload }; this.setState({ loading: true }); @@ -75,23 +77,25 @@ class Index extends Component { col = { ...col, width: 250, render: (text, record) => ( this.handleOpts("view", record)}>{text}) + onClick={() => this.handleOpts("edit", record)}>{text}) }; break; case "paymentType": col = { ...col, width: 100 }; break; case "paymentScope": - col = { ...col, width: 300 }; + col = welfareTypeEnum !== "CUSTOM" ? { ...col, width: 300 } : { + ...col, width: "30%", + render: (__, record) => ({record[`${dataIndex}Span`]}) + }; break; case "remarks": col = { ...col, width: 200 }; break; - case "paymentScopt": case "welfareType": col = { ...col, width: "30%", - render: (__, record) => ({record[`${dataIndex}Span`] || record["paymentScopeSpan"]}) + render: (__, record) => ({record[`${dataIndex}Span`]}) }; break; case "isUse": @@ -113,6 +117,12 @@ class Index extends Component { const { id, schemeName } = record; switch (key) { case "edit": + this.setState({ + welfarePlanEditSlide: { + visible: true, id, welfareTypeEnum: selectedKey, + title: `${id ? getLabel(501169, "编辑") : getLabel(365, "新建")}${_.find(tabWelfarePlanList, o => o.key === selectedKey).title}${getLabel(111, "方案")}` + } + }); break; case "delete": Modal.confirm({ @@ -176,7 +186,7 @@ class Index extends Component { }; render() { - const { dataSource, columns, pageInfo, loading, copyDialog, customDialog } = this.state; + const { dataSource, columns, pageInfo, loading, copyDialog, customDialog, welfarePlanEditSlide } = this.state; const { selectedKey, taxAgentStore: { showOperateBtn } } = this.props; const pagination = { ...pageInfo, @@ -254,6 +264,12 @@ class Index extends Component { customDialog: { ...customDialog, visible: false, customId: "" } }, () => isRefrese && this.getList(this.props))} /> + {/*新建编辑方案*/} + this.setState({ + welfarePlanEditSlide: { ...welfarePlanEditSlide, visible: isFresh } + }, () => isFresh && this.getList(this.props))} + /> ); } diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/config.js b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/config.js index d3b64200..acff5efa 100644 --- a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/config.js +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/config.js @@ -30,14 +30,15 @@ export const renderDropMenuDatas = (selectedKey, showOperateBtn) => { content: getLabel(545781, "操作日志") }]; }; -export const renderReqBtns = (selectedKey, onSalaryOpts, showOperateBtn) => { +export const renderReqBtns = (selectedKey, onSalaryOpts, showOperateBtn, customQuery) => { let reqBtns = [ , onSalaryOpts("OPEN")} onAdvanceSearch={() => onSalaryOpts("SEARCH")}/> ]; switch (selectedKey) { case "CUSTOM": - reqBtns[reqBtns.length - 1] = onSalaryOpts("SEARCH")} + reqBtns[reqBtns.length - 1] = onSalaryOpts("SEARCH", v)} + value={customQuery} options={[ { key: "", showname: getLabel(111, "全部"), selected: true }, { key: "SOCIAL_SECURITY", showname: getLabel(538967, "社保") }, @@ -145,6 +146,73 @@ export const welfarePlanCopyConditions = { } ] }; +export const planConditons = [ + { + items: [ + { + conditionType: "SELECT", + domkey: ["paymentType"], + fieldcol: 14, + label: getLabel(543163, "缴纳类型"), + labelcol: 6, + options: [ + { key: "SCHEME_TOWN", showname: getLabel(19702, "城镇") }, + { key: "SCHEME_VILLAGE", showname: getLabel(19703, "农村") } + ], + value: "SCHEME_TOWN", + rules: "required|string", + viewAttr: 3 + }, + { + conditionType: "INPUT", + domkey: ["schemeName"], + fieldcol: 14, + label: getLabel(33162, "方案名称"), + labelcol: 6, + value: "", + rules: "required|string", + viewAttr: 3 + }, + { + conditionType: "SELECT", + domkey: ["sharedType"], + fieldcol: 14, + label: getLabel(543164, "可见性"), + labelcol: 6, + options: [ + { key: "0", showname: getLabel(111, "公共") }, + { key: "1", showname: getLabel(111, "私有") } + ], + value: "0", + rules: "required|string", + viewAttr: 3 + }, + { + conditionType: "SELECT", + domkey: ["taxAgentIds"], + fieldcol: 14, + label: getLabel(543165, "可见性范围"), + labelcol: 6, + multiple: true, + options: [], + value: "", + hide: true, + viewAttr: 2 + }, + { + conditionType: "TEXTAREA", + domkey: ["remarks"], + fieldcol: 14, + label: getLabel(536726, "备注"), + labelcol: 6, + value: "", + viewAttr: 2 + } + ], + title: getLabel(82743, "基础信息"), + defaultshow: true + } +]; export const customPlanConditons = [ { items: [ @@ -169,6 +237,7 @@ export const customPlanConditons = [ { key: "ACCUMULATION_FUND", showname: getLabel(538969, "公积金") }, { key: "OTHER", showname: getLabel(542717, "企业年金及其他福利") } ], + value: "SOCIAL_SECURITY", detailtype: 3, rules: "required|string", viewAttr: 3 diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.js b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.js index 338de3ee..50ddb40b 100644 --- a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.js +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.js @@ -22,30 +22,31 @@ class Index extends Component { constructor(props) { super(props); this.state = { - selectedKey: "SOCIAL_SECURITY", showSearchAd: false, isQuery: false, logDialogVisible: false + selectedKey: "SOCIAL_SECURITY", showSearchAd: false, isQuery: false, logDialogVisible: false, + customQuery: "" }; this.welfarePlanListRef = null; } - handleReqBtnsCLick = (type) => { + handleReqBtnsCLick = (type, value) => { const { selectedKey } = this.state; switch (type) { case "ADD": const { handleOpts } = this.welfarePlanListRef.wrappedInstance || {}; - selectedKey === "CUSTOM" ? handleOpts("custom-edit") : ""; + handleOpts(selectedKey === "CUSTOM" ? "custom-edit" : "edit"); break; case "OPEN": this.handleOpenAdvanceSearch(); break; case "SEARCH": - this.handleAdvanceSearch(); + this.handleAdvanceSearch(value); break; default: break; } }; handleOpenAdvanceSearch = () => this.setState({ showSearchAd: true }); - handleAdvanceSearch = () => this.setState({ isQuery: !this.state.isQuery }); + handleAdvanceSearch = (customQuery = "") => this.setState({ isQuery: !this.state.isQuery, customQuery }); onAdSearch = () => this.setState({ showSearchAd: false, isQuery: !this.state.isQuery }); onDropMenuClick = (key) => { switch (key) { @@ -59,15 +60,15 @@ class Index extends Component { render() { const { taxAgentStore: { showOperateBtn } } = this.props; - const { logDialogVisible, selectedKey, showSearchAd, isQuery } = this.state; + const { logDialogVisible, selectedKey, showSearchAd, isQuery, customQuery } = this.state; return (
} iconBgcolor="#F14A2D" showDropIcon dropMenuDatas={renderDropMenuDatas(selectedKey, showOperateBtn)} onDropMenuClick={this.onDropMenuClick} tabDatas={tabWelfarePlanList} selectedKey={selectedKey} - onChange={selectedKey => this.setState({ selectedKey, showSearchAd: false })} - buttons={renderReqBtns(selectedKey, this.handleReqBtnsCLick, showOperateBtn)} + onChange={selectedKey => this.setState({ selectedKey, showSearchAd: false, customQuery: "" })} + buttons={renderReqBtns(selectedKey, this.handleReqBtnsCLick, showOperateBtn, customQuery)} >
{/*列表*/} - this.welfarePlanListRef = dom} + this.welfarePlanListRef = dom} customQuery={customQuery} selectedKey={selectedKey} isQuery={isQuery}/>
{/*操作日志*/} diff --git a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.less b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.less index 0e53feaf..59f41a5d 100644 --- a/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.less +++ b/pc4mobx/hrmSalary/pages/socialSecurityBenefits/welfarePlan/index.less @@ -102,6 +102,129 @@ } } +.welfare-plan-edit-layout { + .titleDialog { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 46px 0 16px; + + .titleCol { + flex: 1; + display: flex; + align-items: center; + } + + .titleLeftBox { + .titleIcon { + color: #fff; + margin: 0; + width: 40px; + height: 40px; + line-height: 40px; + font-size: 22px; + display: flex; + align-items: center; + justify-content: center; + background: #F14A2D; + border-radius: 50%; + } + + .title { + font-size: 14px; + color: #333; + padding-left: 6px; + } + } + + .titleRightBox { + justify-content: flex-end; + + button:last-child { + margin-left: 10px; + } + } + } + + .wea-slide-modal-title { + border-bottom: 1px solid #e5e5e5 !important; + } + + .wea-slide-modal-content { + height: 100%; + + .welfare-plan-edit-area { + background: #f6f6f6; + height: 100%; + overflow-y: auto; + padding: 16px; + + .wea-search-group, .wea-form-cell { + padding: 0; + + .wea-form-item { + padding: 5px 16px; + + .wea-form-item-label { + color: #666; + } + } + } + + .wea-form-cell-wrapper { + background: #FFF; + border: 1px solid #e5e5e5; + + .wea-form-cell:not(:last-child) { + border-bottom: 1px solid #e5e5e5; + } + } + + .wea-select, .ant-select, .ant-select-selection { + width: 100%; + } + + .wea-select .wea-select-input .arrow { + position: absolute; + right: 4px; + top: 8px; + color: #666; + } + + .wea-select .wdb { + word-break: break-all !important; + word-wrap: break-word !important; + } + + .wea-select .wea-select-input { + height: 30px; + white-space: nowrap; + min-width: 100px; + max-width: 426.16px; + width: 100%; + display: inline-block; + padding: 4px 17px 4px 4px; + position: relative; + min-height: 30px; + border: 1px solid #d9d9d9; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} + +.zIndex0-welfare-plan { + .wea-new-top-req { + z-index: 0 !important; + } +} + @media screen and (min-width: 1440px) { .salary-welfare-plan-content { .welfare-plan-adapt { diff --git a/pc4mobx/hrmSalary/stores/programme.js b/pc4mobx/hrmSalary/stores/programme.js index ec776699..b8f15b5e 100644 --- a/pc4mobx/hrmSalary/stores/programme.js +++ b/pc4mobx/hrmSalary/stores/programme.js @@ -16,6 +16,8 @@ export class ProgrammeStore { @action initPlanCopyForm = () => this.planCopyForm = new WeaForm();//方案-初始化复制Form @observable planCustomForm = new WeaForm(); //方案-新建编辑自定义方案Form @action initPlanCustomForm = () => this.planCustomForm = new WeaForm();//方案-初始化新建编辑自定义方案Form + @observable hasBeenModify = false; //社保福利方案-员工薪资档数据-是否修改过 + @action setHasBeenModify = (v) => this.hasBeenModify = v;//设置社保福利方案-员工薪资档数据 @observable tableStore = new TableStore(); // new table