Signed-off-by: Logaxn <logaxn@foxmail.com>

This commit is contained in:
Logaxn 2019-03-11 23:37:17 +08:00
parent 49ae4d9aee
commit 08e09565bd
13 changed files with 336 additions and 231 deletions

2
.gitignore vendored
View File

@ -12,7 +12,7 @@ test/e2e/reports
selenium-debug.log selenium-debug.log
# Editor directories and files # Editor directories and files
.idea /.idea/workspace.xml
.vscode .vscode
*.suo *.suo
*.ntvs* *.ntvs*

View File

@ -0,0 +1,24 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="250" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<codeStyleSettings language="HTML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="CLASS_BRACE_STYLE" value="1" />
<option name="METHOD_BRACE_STYLE" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

4
.idea/encodings.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@ -0,0 +1,15 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="slot-scope" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

9
.idea/misc.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="WebPackConfiguration">
<option name="path" value="$PROJECT_DIR$/build/webpack.dev.conf.js" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/zealouscrm-vue.iml" filepath="$PROJECT_DIR$/.idea/zealouscrm-vue.iml" />
</modules>
</component>
</project>

8
.idea/zealouscrm-vue.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -41,7 +41,8 @@ module.exports = {
}, },
module: { module: {
rules: [ rules: [
...(config.dev.useEslint ? [createLintingRule()] : []), // 暂时禁用 Eslint todo
// ...(config.dev.useEslint ? [createLintingRule()] : []),
{ {
test: /\.vue$/, test: /\.vue$/,
loader: 'vue-loader', loader: 'vue-loader',

View File

@ -26,6 +26,8 @@ const messages = {
} }
const i18n = new VueI18n({ const i18n = new VueI18n({
// 暂时隐藏 i18n 警告 todo
silentTranslationWarn: true,
// set locale // set locale
// options: en | zh | es // options: en | zh | es
locale: Cookies.get('language') || 'en', locale: Cookies.get('language') || 'en',

View File

@ -18,14 +18,14 @@
</div> </div>
<el-table <el-table
v-loading="listLoading" v-loading="listLoading"
:key="tableKey" :key="tableKey"
:data="list" :data="list"
border border
fit fit
highlight-current-row highlight-current-row
style="width: 100%;" style="width: 100%;"
@sort-change="sortChange"> @sort-change="sortChange">
<el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="65"> <el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="65">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.id }}</span> <span>{{ scope.row.id }}</span>
@ -42,7 +42,7 @@
<el-tag>{{ scope.row.type | typeFilter }}</el-tag> <el-tag>{{ scope.row.type | typeFilter }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('table.author')" width="110px" align="center"> <el-table-column :label="$t('giftList.giftName')" width="110px" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.author }}</span> <span>{{ scope.row.author }}</span>
</template> </template>
@ -81,7 +81,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" /> <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList"/>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible"> <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;"> <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
@ -128,235 +128,247 @@
</template> </template>
<script> <script>
import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article' import {fetchList, fetchPv, createArticle, updateArticle} from '@/api/article'
import waves from '@/directive/waves' // Waves directive import waves from '@/directive/waves' // Waves directive
import { parseTime } from '@/utils' import {parseTime} from '@/utils'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
const calendarTypeOptions = [ import local from './local'
{ key: 'CN', display_name: 'China' },
{ key: 'US', display_name: 'USA' },
{ key: 'JP', display_name: 'Japan' },
{ key: 'EU', display_name: 'Eurozone' }
]
// arr to obj ,such as { CN : "China", US : "USA" } const viewName = 'giftList'
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name
return acc
}, {})
export default { const calendarTypeOptions = [
name: 'ComplexTable', {key: 'CN', display_name: 'China'},
components: { Pagination }, {key: 'US', display_name: 'USA'},
directives: { waves }, {key: 'JP', display_name: 'Japan'},
filters: { {key: 'EU', display_name: 'Eurozone'}
statusFilter(status) { ]
const statusMap = {
published: 'success', // arr to obj ,such as { CN : "China", US : "USA" }
draft: 'info', const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
deleted: 'danger' acc[cur.key] = cur.display_name
return acc
}, {})
export default {
name: 'ComplexTable',
components: {Pagination},
directives: {waves},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
},
typeFilter(type) {
return calendarTypeKeyValue[type]
} }
return statusMap[status]
}, },
typeFilter(type) { data() {
return calendarTypeKeyValue[type] return {
} tableKey: 0,
}, list: null,
data() { total: 0,
return { listLoading: true,
tableKey: 0, listQuery: {
list: null, page: 1,
total: 0, limit: 20,
listLoading: true, importance: undefined,
listQuery: { title: undefined,
page: 1, type: undefined,
limit: 20, sort: '+id'
importance: undefined, },
title: undefined, importanceOptions: [1, 2, 3],
type: undefined, calendarTypeOptions,
sort: '+id' sortOptions: [{label: 'ID Ascending', key: '+id'}, {label: 'ID Descending', key: '-id'}],
}, statusOptions: ['published', 'draft', 'deleted'],
importanceOptions: [1, 2, 3], showReviewer: false,
calendarTypeOptions, temp: {
sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }], id: undefined,
statusOptions: ['published', 'draft', 'deleted'], importance: 1,
showReviewer: false, remark: '',
temp: { timestamp: new Date(),
id: undefined, title: '',
importance: 1, type: '',
remark: '', status: 'published'
timestamp: new Date(), },
title: '', dialogFormVisible: false,
type: '', dialogStatus: '',
status: 'published' textMap: {
}, update: 'Edit',
dialogFormVisible: false, create: 'Create'
dialogStatus: '', },
textMap: { dialogPvVisible: false,
update: 'Edit', pvData: [],
create: 'Create' rules: {
}, type: [{required: true, message: 'type is required', trigger: 'change'}],
dialogPvVisible: false, timestamp: [{type: 'date', required: true, message: 'timestamp is required', trigger: 'change'}],
pvData: [], title: [{required: true, message: 'title is required', trigger: 'blur'}]
rules: { },
type: [{ required: true, message: 'type is required', trigger: 'change' }], downloadLoading: false
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }], }
title: [{ required: true, message: 'title is required', trigger: 'blur' }] },
}, created() {
downloadLoading: false if (!this.$i18n.getLocaleMessage('en')[viewName]) {
} for (let lang in local) {
}, const obj = {}
created() { obj[viewName] = local[lang]
this.getList() this.$i18n.mergeLocaleMessage(lang, obj)
}, }
methods: { }
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
// Just to simulate the time of the request
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
},
handleFilter() {
this.listQuery.page = 1
this.getList() this.getList()
}, },
handleModifyStatus(row, status) { methods: {
this.$message({ getList() {
message: '操作成功', this.listLoading = true
type: 'success' fetchList(this.listQuery).then(response => {
}) this.list = response.data.items
row.status = status this.total = response.data.total
},
sortChange(data) { // Just to simulate the time of the request
const { prop, order } = data setTimeout(() => {
if (prop === 'id') { this.listLoading = false
this.sortByID(order) }, 1.5 * 1000)
}
},
sortByID(order) {
if (order === 'ascending') {
this.listQuery.sort = '+id'
} else {
this.listQuery.sort = '-id'
}
this.handleFilter()
},
resetTemp() {
this.temp = {
id: undefined,
importance: 1,
remark: '',
timestamp: new Date(),
title: '',
status: 'published',
type: ''
}
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
this.temp.author = 'vue-element-admin'
createArticle(this.temp).then(() => {
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.temp)
tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
updateArticle(tempData).then(() => {
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.temp)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDelete(row) {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData
this.dialogPvVisible = true
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
const data = this.formatJson(filterVal, this.list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: 'table-list'
}) })
this.downloadLoading = false },
}) handleFilter() {
}, this.listQuery.page = 1
formatJson(filterVal, jsonData) { this.getList()
return jsonData.map(v => filterVal.map(j => { },
if (j === 'timestamp') { handleModifyStatus(row, status) {
return parseTime(v[j]) this.$message({
} else { message: '操作成功',
return v[j] type: 'success'
})
row.status = status
},
sortChange(data) {
const {prop, order} = data
if (prop === 'id') {
this.sortByID(order)
} }
})) },
sortByID(order) {
if (order === 'ascending') {
this.listQuery.sort = '+id'
} else {
this.listQuery.sort = '-id'
}
this.handleFilter()
},
resetTemp() {
this.temp = {
id: undefined,
importance: 1,
remark: '',
timestamp: new Date(),
title: '',
status: 'published',
type: ''
}
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
this.temp.author = 'vue-element-admin'
createArticle(this.temp).then(() => {
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.temp)
tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
updateArticle(tempData).then(() => {
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.temp)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDelete(row) {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData
this.dialogPvVisible = true
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
const data = this.formatJson(filterVal, this.list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: 'table-list'
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
} }
} }
}
</script> </script>

11
src/views/gift/local.js Normal file
View File

@ -0,0 +1,11 @@
export default {
en: {
giftName: 'Name'
},
zh: {
giftName: '礼品'
},
es: {
giftName: 'Gift'
}
}