Javascript v-model未使用vuejs中的开关和选择下拉菜单更改数据
在laravel和vue的帮助下,我正在用crud生成器制作一个动态管理面板 我有一个从API异步加载数据的表。在我的表格中有一个is_特色列,我想成为一个开关。这样,用户就可以从表格页面更改值,而不是转到编辑页面 要生成整个表,有一个配置对象,其中包含要显示的字段以及该字段的类型和其他元数据。在配置对象中,有一个名为Javascript v-model未使用vuejs中的开关和选择下拉菜单更改数据,javascript,php,laravel,vue.js,axios,Javascript,Php,Laravel,Vue.js,Axios,在laravel和vue的帮助下,我正在用crud生成器制作一个动态管理面板 我有一个从API异步加载数据的表。在我的表格中有一个is_特色列,我想成为一个开关。这样,用户就可以从表格页面更改值,而不是转到编辑页面 要生成整个表,有一个配置对象,其中包含要显示的字段以及该字段的类型和其他元数据。在配置对象中,有一个名为prerender的字段,负责预渲染需要调用其他API的字段或一些可编辑字段,如选择下拉列表或开关 要使开关和选择字段工作,我有一个名为fieldData的空对象。 当找到类型为t
prerender
的字段,负责预渲染需要调用其他API的字段或一些可编辑字段,如选择下拉列表或开关
要使开关和选择字段工作,我有一个名为fieldData
的空对象。
当找到类型为type:boolean
和prerender:true
的字段时,我的代码将以field.name作为字段数据中的属性名称初始化属性,并用该字段名称下的相应值填充该属性
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
但开关和选择按钮在此不起作用
所以我需要帮助
这是我的全部代码供参考
<template>
<div class="app-container">
<el-row :gutter="20" style="display: flex; align-items: center;">
<el-col :span="10">
<h1 style="text-transform: capatilize;">{{ resourceName }}</h1>
</el-col>
<el-col :span="14" style="display: flex; justify-content: flex-end; align-items: center">
<el-input
v-model="navigation.search"
placeholder="Search anything here"
prefix-icon="el-icon-search"
style="width: 300px; margin: 0 10px;"
@keydown.enter.native="handleGlobalSearch"
/>
<FilterPannel
style="margin: 0 10px"
:filter-pannel-obj="filterPannelObj"
@set-filter="handleFilterVals"
@reset-filter="getTableData({})"
/>
<Import
:url="`/api/${resourceName}/upload`"
@import-success="handleImportSucces"
@import-error="handleImportError"
/>
<Export :url="`/api/${resourceName}/export`" :selected-ids="selected.map(el => el.id)" />
<el-button
type="info"
icon="el-icon-delete"
@click="$refs['table'].clearSelection()"
>Clear Selection</el-button>
<el-button type="danger" icon="el-icon-delete" @click="handleMultipleDelete">Delete Selected</el-button>
</el-col>
</el-row>
<el-row>
<el-table
ref="table"
v-loading="loading.tableData"
:data="tableData"
border
:row-key="getRowKeys"
@sort-change="handleSortChange"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" label="Selection" reserve-selection />
<el-table-column label="Actions" width="200">
<template slot-scope="scope">
<div style="display: flex; justify-content: space-around;">
<el-button
icon="el-icon-view"
type="primary"
circle
@click="$router.push(`/${resourceName}/view/${scope.row.id}`)"
/>
<el-button
icon="el-icon-edit"
type="success"
circle
@click="$router.push(`/${resourceName}/edit/${scope.row.id}`)"
/>
<el-button
icon="el-icon-delete"
type="danger"
circle
@click="handleDeleteClick(scope.row.id)"
/>
</div>
</template>
</el-table-column>
<el-table-column
v-for="field in fieldsToShow"
:key="field.name"
:prop="field.name"
:label="field.name.replace('_',' ')"
sortable="custom"
>
<template slot-scope="scope">
<div
v-if="field.type=='multilangtext'"
class="cell"
>{{ JSON.parse(scope.row[field.name])[$store.state.app.language] }}</div>
<div v-if="field.type=='text'" class="cell">{{ scope.row[field.name] }}</div>
<el-tag v-if="field.type=='tag'">{{ scope.row.type }}</el-tag>
<img v-if="field.type=='image'" :src="scope.row.icon" width="100px" height="100px" />
<div v-if="field.type=='oneFrom'" class="cell">
<el-tag
:key="scope.row[field.name]"
>{{field.multilang ? scope.row[field.name][$store.state.app.language] : scope.row[field.name]}}</el-tag>
</div>
<div v-if="field.type=='manyFrom'" class="cell">
<el-tag
style="margin: 5px"
v-for="(item, index) in scope.row[field.name]"
:key="`${field.multilang ? item[$store.state.app.language] : item}+${index}`"
>{{field.multilang ? item[$store.state.app.language] : item}}</el-tag>
</div>
<div v-if="field.type=='boolean'" class="cell">
{{scope.row.id}} =>
{{field.name}} =>>
{{scope.row[field.name]}} =>>>
{{fieldData[field.name][scope.row.id]}}
<el-switch
:key="`switch${scope.row.id}`"
v-model="fieldData[field.name][scope.row.id]"
:active-value="1"
:inactive-value="0"
></el-switch>
</div>
<div v-if="field.type=='select'" class="cell">
<el-select :key="`select${scope.row.id}`" v-model="fieldData[field.name][scope.row.id]" placeholder="Select">
<el-option
v-for="item in field.options"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row>
<pagination
style="padding: 0;"
:total="paginationData.total"
:page.sync="paginationData.current_page"
:limit.sync="paginationData.per_page"
@pagination="handlePagination"
/>
</el-row>
</div>
</template>
<script>
import Pagination from '@/components/Pagination';
import FilterPannel from '@/components/FilterPannel';
import Export from './components/Export'; // ? not needed
import Import from './components/Import';
import axios from 'axios';
import Resource from '@/api/resource';
const resourceName = 'coupons';
const ResourceApi = new Resource(resourceName);
export default {
name: 'CategoryList',
components: {
Pagination,
FilterPannel,
Export,
Import,
},
data() {
return {
resourceName: resourceName,
language: 'en',
tableData: [],
fieldData: {},
fieldsToShow: [
{ name: 'title', type: 'multilangtext' },
// { name: 'description', type: 'multilangtext' },
{ name: 'code', type: 'text' },
// { name: 'expiry_date', type: 'text' },
{ name: 'type', type: 'tag' },
{
name: 'store_id',
type: 'oneFrom',
url: '/api/stores/',
foreignKey: 'store_id',
attrName: 'name',
multilang: true,
prerender: true,
},
{
name: 'tags',
type: 'manyFrom',
url: '/api/tags?idsarr=',
foreignKey: 'tags',
attrName: 'name',
multilang: true,
prerender: true,
},
{
name: 'brands',
type: 'manyFrom',
url: '/api/brands?idsarr=',
foreignKey: 'brands',
attrName: 'name',
multilang: true,
prerender: true,
},
// { name: 'brands', type: 'text' },
{ name: 'is_featured', type: 'boolean', prerender: true },
{
name: 'status',
type: 'select',
options: ['publish', 'draft', 'trash'],
prerender: true,
},
],
paginationData: {
current_page: 0,
last_page: 0,
per_page: 0,
total: 0,
},
navigation: {
page: 1,
limit: 10,
sort: '',
'sort-order': 'asc',
filters: '',
search: '',
},
filters: '',
selected: [], // ? for selection
loading: {
tableData: false,
},
allData: [],
filterPannelObj: {
title: {
default: '',
type: 'Input',
label: 'Title',
},
description: {
default: '',
type: 'Input',
label: 'Description',
},
promo_text: {
default: '',
type: 'Input',
label: 'Promo Text',
},
type: {
default: [],
type: 'checkbox',
label: 'Type',
src: [
{ value: 'coupon', label: 'Coupon' },
{ value: 'offer', label: 'Offer' },
],
},
code: {
default: '',
type: 'Input',
label: 'Code',
},
store_id: {
default: [],
type: 'select',
label: 'Store',
src: [],
multiple: true,
},
brands: {
default: [],
type: 'select',
label: 'Brands',
src: [],
multiple: true,
},
tags: {
default: [],
type: 'select',
label: 'Tags',
src: [],
multiple: true,
},
cats: {
default: [],
type: 'select',
label: 'Categories',
src: [],
multiple: true,
},
},
};
},
watch: {
async 'navigation.search'(newVal, oldVal) {
await this.handleGlobalSearch();
},
},
async created() {
await this.getTableData({});
this.allData = await ResourceApi.list({ limit: -1 });
// to fill filter dialog selects
// To get brands
this.filterPannelObj.brands.src = (await axios.get(
`/api/brands?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get tags
this.filterPannelObj.tags.src = (await axios.get(
`/api/tags?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get categories
this.filterPannelObj.cats.src = (await axios.get(
`/api/categories?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get stores
this.filterPannelObj.store_id.src = (await axios.get(
`/api/stores?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
},
methods: {
printScope(x) {
console.log('TCL: printScope -> x', x);
},
async getTableData(query) {
this.loading.tableData = true;
const responseData = await ResourceApi.list(query);
this.tableData = responseData.data;
this.paginationData = this.pick(
['current_page', 'last_page', 'per_page', 'total'],
responseData
);
Object.keys(this.paginationData).forEach(
key => (this.paginationData[key] = parseInt(this.paginationData[key]))
);
await this.handlePrerender();
this.loading.tableData = false;
},
async handlePrerender() {
this.fieldsToShow.forEach(async field => {
if (field.prerender) {
switch (field.type) {
case 'oneFrom': {
await this.setRelatedFieldName(field);
break;
}
case 'manyFrom': {
await this.setRelatedFieldName(field);
break;
}
case 'boolean': {
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
break;
}
case 'select': {
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
break;
}
}
}
});
},
// utils
pick(propsArr, srcObj) {
return Object.keys(srcObj).reduce((obj, k) => {
if (propsArr.includes(k)) {
obj[k] = srcObj[k];
}
return obj;
}, {});
},
// ? remember to refactor the parameter id
async setRelatedFieldName({
name,
type,
url,
foreignKey,
attrName,
multilang,
}) {
this.tableData.forEach(async data => {
if (type === 'oneFrom') {
data[name] = (await axios.get(`${url}${data[foreignKey]}`)).data[
attrName
];
if (multilang) {
data[name] = JSON.parse(data[name]);
}
} else if (type === 'manyFrom') {
data[name] = (await axios.get(`${url}${data[foreignKey]}`)).data.map(
idata => (multilang ? JSON.parse(idata[attrName]) : idata[attrName])
);
}
});
},
// Sort
async handleSortChange(change) {
this.navigation.sort = change.prop;
if (change.order === 'ascending') {
this.navigation['sort-order'] = 'asc';
} else if (change.order === 'descending') {
this.navigation['sort-order'] = 'desc';
}
await this.getTableData(this.navigation);
},
// Pagination
async handlePagination(obj) {
// obj page obj containing {page: ..., limit: ...}
this.navigation.page = obj.page;
this.navigation.limit = obj.limit;
await this.getTableData(this.navigation);
},
// Global Search
async handleGlobalSearch() {
await this.getTableData(this.navigation);
},
// ? Skipped for now
// Filters
async handleFilterVals(filterparams) {
console.log('TCL: handleFilterVals -> filterparams', filterparams);
this.navigation.filters = JSON.stringify(filterparams.filters);
this.navigation.sort = filterparams.sort.field;
this.navigation.sort = 'name';
this.navigation['sort-order'] = filterparams.sort.asc ? 'asc' : 'desc';
await this.getTableData(this.navigation);
},
async handleDeleteClick(id) {
ResourceApi.destroy(id)
.then(res => {
this.$message.success('Delete Successfully');
this.getTableData({ page: this.paginationData.current_page });
})
.error(err => {
this.$message.error(err);
});
},
// Selection methods
handleSelectionChange(selection) {
this.selected = selection;
},
getRowKeys(row) {
return row.id;
},
// Multiple Delete
handleMultipleDelete() {
axios
.delete(
`/api/${this.resourceName}/delete-multiple?ids=${this.selected
.map(item => item.id)
.join(',')}`
)
.then(async () => {
this.$message.success('Records deleted successfully');
await this.getTableData({ page: this.paginationData.current_page });
if (this.tableData.length === 0) {
await this.getTableData({
page: this.paginationData.current_page,
});
}
this.$refs.table.clearSelection();
})
.catch();
},
// Import Events
handleImportSucces() {
this.$message.success('New Data Imported');
this.getTableData({});
},
handleImportError(err) {
this.$message.error('There were some errors. CHK console');
console.log(err);
},
},
};
</script>
<style lang="scss" scoped>
</style>
{{resourceName}}
清晰选择
删除所选内容
{{JSON.parse(scope.row[field.name])[$store.state.app.language]}
{{scope.row[field.name]}
{{scope.row.type}
{{field.multilang?scope.row[field.name][$store.state.app.language]:scope.row[field.name]}
{{field.multilang?项[$store.state.app.language]:项}
{{scope.row.id}=>
{{field.name}}=>>
{{scope.row[field.name]}}=>>
{{fieldData[field.name][scope.row.id]}
从“@/components/Pagination”导入分页;
从“@/components/FilterPanel”导入FilterPanel;
从“./components/Export”导入导出;//?不需要
从“./components/import”导入;
从“axios”导入axios;
从“@/api/Resource”导入资源;
const resourceName=‘优惠券’;
const ResourceApi=新资源(resourceName);
导出默认值{
名称:'类别列表',
组成部分:{
标页码
过滤板,
出口,,
进口,,
},
数据(){
返回{
resourceName:resourceName,
语言:"en",,
tableData:[],
字段数据:{},
现场演示:[
{name:'title',键入:'multilangtext'},
//{name:'description',键入:'multilangtext'},
{name:'code',键入:'text'},
//{name:'expiration_date',键入:'text'},
{name:'type',type:'tag'},
{
名称:'store_id',
键入:“oneFrom”,
url:“/api/stores/”,
foreignKey:“存储\u id”,
attrName:'name',
是的,
预渲染:正确,
},
{
名称:'标签',
键入:“manyFrom”,
url:“/api/tags?idsarr=”,
外键:“标签”,
attrName:'name',
是的,
预渲染:正确,
},
{
名称:'品牌',
键入:“manyFrom”,
url:“/api/brands?idsarr=”,
外键:“品牌”,
attrName:'name',
是的,
预渲染:正确,
},
//{name:'brands',键入:'text'},
{name:'is_featured',type:'boolean',prerender:true},
{
名称:'状态',
键入:“选择”,
选项:[“发布”、“草稿”、“垃圾箱”],
预渲染:正确,
},
],
分页数据:{
当前页面:0,
最后一页:0,
每页:0,
总数:0,
},
导航:{
页码:1,
限额:10,
排序:“”,
“排序顺序”:“asc”,
筛选器:“”,
搜索:“”,
},
筛选器:“”,
已选择:[],/?供选择
装载:{
tableData:false,
},
所有数据:[],
FilterPanelobj:{
标题:{
默认值:“”,
键入:“输入”,
标签:“标题”,
},
说明:{
默认值:“”,
键入:“输入”,
标签:“说明”,
},
促销文本:{
默认值:“”,
键入:“输入”,
标签:“促销文本”,
},
类型:{
默认值:[],
键入:“复选框”,
标签:“类型”,
src:[
{值:'优惠券',标签:'优惠券'},
{值:'offer',标签:'offer'},
],
},
代码:{
默认值:“”,
键入:“输入”,
标签:“代码”,
},
门店标识:{
默认值:[],
键入:“选择”,
标签:'商店',
src:[],
多重:对,
},
品牌:{
默认值:[],
键入:“选择”,
标签:“品牌”,
src:[],
多重:对,
},
标签:{
默认值:[],
键入:“选择”,
标签:“标签”,
src:[],
多重:对,
},
猫:{
默认值:[],
键入:“选择”,
标签:“类别”,
src:[],
多重:对,
},
},
};
},
观察:{
异步“navigation.search”(newVal、oldVal){
等待此消息。handleGlobalSearch();
},
},
异步创建(){
等待这个.getTableData({});
this.allData=await ResourceApi.list({limit:-1});
//填补空缺
this.fieldData[field.name] = {};
self.$set(self.fieldData,field.name,{});
this.fieldData[field.name][data.id] = data[field.name] // to
//Replace
self.$set(self.fieldData[field.name],data.id, data[field.name]);