Commit c0651547 authored by 我在何方's avatar 我在何方

添加重货,泡货特价申请

parents 3493af0c 38192b11
Stack trace:
Frame Function Args
00800000010 0018006401E (001802839A0, 0018026EFD1, 00000000059, 000FFFFB700)
00800000010 0018004973A (00800000010, 00100000000, 00000000000, 00000000001)
00800000010 00180049772 (00000000000, 00000000000, 00000000059, 00180354100)
00800000010 00180070FE9 (00000000000, 000FFFFC7D0, 0000000000B, 00000000000)
00800000010 001800712A0 (00000000003, 000FFFFC8F0, 00180045BCF, 000FFFFC8F0)
00800000010 00180072AB9 (000FFFFC8F0, 00180271795, 001801004A7, 0000000000D)
00800000010 0018005B2A3 (7FF8018B1490, 00000000000, 00000000000, 000FFFFFFFF)
00800000010 0018005C655 (00800029390, 1D400000000, 00000000000, 00000000008)
00800000010 0018005CB3F (000FFFFCCE0, 000FFFFCC45, 00000000000, 205C745C615C635C)
000FFFFCBBC 0018005CF2F (635C655C205C265C, 3E5C205C6F5C685C, 765C655C645C2F5C, 795C745C745C2F5C)
000FFFFCCE0 00180049EE8 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 00180048846 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 001800488F4 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace
Stack trace:
Frame Function Args
00800000010 0018006401E (001802839A0, 0018026EFD1, 00000000059, 000FFFFB710)
00800000010 0018004973A (00800000010, 00100000000, 00000000000, 00000000001)
00800000010 00180049772 (00000000000, 00000000000, 00000000059, 00180353FD0)
00800000010 00180070FE9 (00000000002, 000FFFFC7E0, 0000000000B, 00000000000)
00800000010 001800712A0 (000FFFFCBC0, 00800000010, 00180045B55, 000FFFFC900)
00800000010 00180072AB9 (000FFFFC900, 00180271795, 001801004A7, 0000000000D)
00800000010 0018005B2A3 (7FF8018B1490, 00000000000, 00000000000, 040FFFFFFFF)
00800000010 0018005C655 (00000000002, 00180350D08, 00000000002, 00000000008)
00800000010 0018005CB3F (000FFFFCC9C, 000FFFFCCE0, 000FFFFCC9D, 000FFFFCC00)
000FFFFCBCC 0018005CF2F (6D5C2D5C6C5C615C, 675C615C6E5C615C, 635C2D5C725C655C, 205C655C725C6F5C)
000FFFFCCE0 00180049EE8 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 00180048846 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 001800488F4 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace
......@@ -186,6 +186,16 @@ export function getBillService(params) {
});
}
// 打包下载提货单
export function zipDownload(params) {
return request({
url: "/shipment/make-bill-of-lading/zipDownload",
responseType: "arraybuffer",
method: "get",
params,
});
}
/**
* formData数据
*
......
......@@ -206,6 +206,7 @@ export function receiptExportExcel(query) {
return request({
url: '/ecw/receipt/export-excel',
method: 'get',
responseType: 'blob',
params: query
})
}
......
......@@ -61,3 +61,12 @@ export function setUnauthCustomerFeeType(data) {
data: data
})
}
//
export function getFeeTypeByCustomerProduct(data){
return request({
url: '/ecw/product-brank/getFeeTypeByCustomerProduct',
method: 'post',
data
})
}
import request from '@/utils/request'
// 查询公告列表
export function panelData() {
return request({
url: '/system/panel/panelData',
method: 'get'
})
}
\ No newline at end of file
<template>
<selector v-model="valueSync" :options="countryList" value-field="tel" key-field="id" :label-field="item => item.nameZh + item.tel" filterable clearable></selector>
<selector v-model="valueSync" :options="countryList" value-field="tel" key-field="id" :label-field="item => item.nameZh + item.tel" filterable clearable :disabled="disabled"></selector>
</template>
<script>
import {getCountryListAll} from '@/api/ecw/country'
......@@ -8,6 +8,7 @@ export default {
components: {selector},
props: {
value: String,
disabled: Boolean
},
data(){
return {
......
<template>
<div>
<el-select v-model="formData.country" :disabled="readonly">
<el-option v-for="(item) in treeList" :value="item.id" :label="item.titleZh" :key="item.id" />
<el-option v-for="(item) in treeList" :value="item.id" :label="$l(item, 'title')" :key="item.id" />
</el-select>
<el-select v-model="formData.province" class="ml-10" :disabled="readonly">
<el-option v-for="(item) in provinceList" :value="item.id" :label="item.titleZh" :key="item.id" />
<el-option v-for="(item) in provinceList" :value="item.id" :label="$l(item, 'title')" :key="item.id" />
</el-select>
<el-select v-model="formData.city" class="ml-10" :disabled="readonly">
<el-option v-for="(item) in cityList" :value="item.id" :label="item.titleZh" :key="item.id" />
<el-option v-for="(item) in cityList" :value="item.id" :label="$l(item, 'title')" :key="item.id" />
</el-select>
</div>
</template>
......
<template>
<el-dialog title="选择联系人" visible :before-close="closeDialog" :close-on-click-modal="false">
<div class="header mb-10">
<div class="flex-center">关键字:</div>
<el-input v-model="form.searchKey" placeholder="" class="w-200"></el-input>
<el-button type="primary" class="ml-10" @click="handleQuery">搜索</el-button>
</div>
<div class="list">
<div class="list-item" v-for="item in list" :key="item.customerContactsId" @click="choose(item)">
<div class="item-box">
<div class="line">
<div class="label">姓名:</div>
<div class="value">{{item.contactsName}}</div>
</div>
<div class="line">
<div class="label">电话:</div>
<div class="value">{{item.areaCode}} {{item.phoneNew}}</div>
</div>
<div class="line">
<div class="label">邮箱:</div>
<div class="value">{{item.emial}}</div>
</div>
<div class="line">
<div class="label">公司:</div>
<div class="value">{{item.company}}</div>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script>
import {getCustomerContactsSelect} from '@/api/ecw/customerContacts'
export default {
props:{
type: Number
},
data(){
return {
show: true,
form:{
/* pageNo: 1,
pageSize: 20, */
searchKey: ''
},
list:[]
}
},
created(){
this.show = true
this.loadList()
},
methods:{
handleQuery(){
// this.form.pageNo = 1
this.loadList()
},
loadList(){
getCustomerContactsSelect(this.form).then(res => {
this.list = res.data
})
},
closeDialog(){
this.show = false
this.$emit('close');
},
choose(contact){
this.$emit('choose', contact)
}
}
}
</script>
<style lang="scss" scoped>
.header{
display: flex;
}
.list{
display: flex;
flex-wrap: wrap;
justify-content: center;
&-item{
background: #eee;
width: 300px;
margin: 10px;
padding: 5px;
border-radius: 10px;
border: 5px solid transparent;
background: linear-gradient(white,white) padding-box,repeating-linear-gradient(-45deg, red 0, red 12.5%, transparent 0, transparent 25%, #58a 0, #58a 37.5%, transparent 0, transparent 50%) 0/5em 5em;
.item-box{
/* background: #fbfaf5; */
padding: 20px;
}
.line{
display: flex;
/* .label{
width: 100px;
} */
.value{
flex: 1;
margin-left: 10px;
}
}
}
}
</style>
......@@ -6,7 +6,7 @@
clearable
remote
reserve-keyword
placeholder="请输入关键词"
:placeholder="$t('请输入关键词')"
:remote-method="remoteMethod"
:loading="loading">
<el-option
......@@ -16,17 +16,17 @@
:value="index">
</el-option>
</el-select>
<el-button v-if="quickable" type="text" @click="showQuickCreate=true" class="ml-10">快速新建</el-button>
<el-button v-if="quickable" type="text" @click="showQuickCreate=true" class="ml-10">{{$t('快速新建')}}</el-button>
<!-- <quick-create v-if="showQuickCreate" @success="onQuickCreateSuccess" @close="showQuickCreate=false" :default="{type}"></quick-create> -->
</div>
</template>
<script>
import {getCustomerContactsSelect, getCustomerContactsListByCustomer} from '@/api/ecw/customerContacts'
import QuickCreate from '@/views/ecw/customer/components/quickCreate'
import QuickCreateCustomer from '@/components/QuickCreateCustomer'
import Vue from 'vue'
export default {
components:{QuickCreate},
components:{QuickCreateCustomer},
props:{
value: [String, Number],
quickable: {
......@@ -75,7 +75,7 @@ export default {
},
showQuickCreate(){
if(!this.showQuickCreate)return
const QuickCreateComp = Vue.extend(QuickCreate)
const QuickCreateComp = Vue.extend(QuickCreateCustomer)
const dialog = new QuickCreateComp({
propsData:{
default: {type: this.type}
......@@ -128,7 +128,7 @@ export default {
if(index < 0){
getCustomerContactsSelect({ids: this.value}).then(res => {
if(!res.data || !res.data.length){
return this.$message.error('联系人信息获取失败')
return this.$message.error(this.$t('联系人信息获取失败'))
}
this.list.unshift(res.data[0])
this.index = 0
......@@ -149,7 +149,7 @@ export default {
this.showQuickCreate = false
getCustomerContactsListByCustomer({customerId: id}).then(res => {
if(!res.data || !res.data.length){
return this.$message.error('联系人信息获取失败')
return this.$message.error(this.$t('联系人信息获取失败'))
}
let data = res.data[0]
data.contactsName = data.name // 字段名跟getCustomerContactsSelect对齐
......
......@@ -3,9 +3,9 @@
<el-col :span="10">
<el-card>
<div slot="header" class="header">
<el-input v-model="queryParams.searchKey" placeholder="用户名/手机/邮箱" style="width:200px" />
<el-input v-model="queryParams.searchKey" :placeholder="$t('用户名/手机/邮箱')" style="width:200px" />
<!-- <dict-selector :type="DICT_TYPE.USER_TYPE" v-model="queryParams.customerType" style="width:100px" /> -->
<el-button type="primary" @click="reLoad">搜索</el-button>
<el-button type="primary" @click="reLoad">{{$t('搜索')}}</el-button>
</div>
<div class="list">
<template v-for="item in list" >
......@@ -20,7 +20,7 @@
<el-col :span="10">
<el-card>
<div slot="header" class="header">
已选客户
{{$t('已选客户')}}
</div>
<div class="list">
<div class="item" v-for="item in choosedList" :key="item.customerContactsId">
......
......@@ -5,7 +5,7 @@
clearable
remote
reserve-keyword
placeholder="请输入关键词"
:placeholder="$t('请输入关键词')"
:remote-method="remoteMethod"
:loading="loading">
<el-option
......
......@@ -3,9 +3,9 @@
<el-col :span="10">
<el-card>
<div slot="header" class="header">
<el-input v-model="queryParams.searchKey" placeholder="用户名/手机/邮箱" style="width:200px" />
<el-input v-model="queryParams.searchKey" :placeholder="$t('用户名/手机/邮箱')" style="width:200px" />
<dict-selector :type="DICT_TYPE.USER_TYPE" v-model="queryParams.customerType" style="width:100px" />
<el-button type="primary" @click="reLoad">搜索</el-button>
<el-button type="primary" @click="reLoad">{{$t('搜索')}}</el-button>
</div>
<div class="list">
<div class="item" v-for="item in list" :key="item.id">
......@@ -18,7 +18,7 @@
<el-col :span="10">
<el-card>
<div slot="header" class="header">
已选客户
{{$t('已选客户')}}
</div>
<div class="list">
<div class="item" v-for="item in choosedList" :key="item.id">
......@@ -94,7 +94,7 @@ export default {
},
loadNextPage(){
if(this.page >= this.pages){
return this.$message.error('已加载全部')
return this.$message.info(this.$t('已加载全部'))
}
this.queryParams.page ++
this.getList()
......
......@@ -2,13 +2,13 @@
<div class="dict-selector">
<el-select v-if="formType == 'select'" v-model="valueSync" :placeholder="placeholder" clearable :multiple="multiple" :disabled="disabled" @change="val => $emit('change', val)">
<el-option v-for="dict in formattedList"
:key="dict.value" :label="dict.label" :value="dict.value"/>
:key="dict.value" :label="$l(dict, 'label')" :value="dict.value"/>
</el-select>
<el-radio-group v-if="formType == 'radio'" v-model="valueSync" :disabled="disabled">
<el-radio v-for="dict in formattedList" :label="dict.value" :checked="valueSync === dict.value" :key="dict.value">{{dict.label}}</el-radio>
<el-radio v-for="dict in formattedList" :label="dict.value" :checked="valueSync === dict.value" :key="dict.value">{{$l(dict, 'label')}}</el-radio>
</el-radio-group>
<el-checkbox-group v-if="formType == 'checkbox'" v-model="valueSync" :disabled="disabled">
<el-checkbox v-for="dict in formattedList" :label="dict.value" :key="dict.value">{{dict.label}}</el-checkbox>
<el-checkbox v-for="dict in formattedList" :label="dict.value" :key="dict.value">{{$l(dict, 'label')}}</el-checkbox>
</el-checkbox-group>
</div>
</template>
......@@ -68,6 +68,7 @@ export default {
this.dictList.forEach(item => {
arr.push({
label: item.label,
labelEn: item.labelEn,
value: this.format(item.value),
cssClass: item.cssClass,
colorType: item.colorType
......
......@@ -3,10 +3,10 @@
<template v-for="(dict, index) in this.getDictDatas2(type, value)">
<!-- 默认样式 -->
<span v-if="dict.colorType === 'default' || dict.colorType === '' || dict.colorType === undefined" :key="dict.value" :index="index"
:class="dict.cssClass">{{ dict.label }}</span>
:class="dict.cssClass">{{ $l(dict, 'label') }}</span>
<!-- Tag 样式 -->
<el-tag v-else :disable-transitions="true" :key="dict.value" :index="index" :type="dict.colorType" :class="dict.cssClass">
{{ dict.label }}
{{ $l(dict, 'label') }}
</el-tag>
</template>
</span>
......
<template>
<div class="need-know" :id="'need-know_' + keyname" v-if="detail">
<!-- <h2>{{detail.titleZh}}</h2> -->
<div class="body" ref="body" v-html="detail.contentZh"></div>
<div class="body" ref="body" v-html="$l(detail, 'content')"></div>
</div>
</template>
<script>
......
......@@ -5,7 +5,7 @@
clearable
remote
reserve-keyword
placeholder="请输入商品关键词"
:placeholder="$t('请输入商品关键词')"
:disabled="disabled"
:remote-method="remoteMethod"
@focus="remoteMethod()"
......
<template>
<el-row class="" :gutter="10">
<el-col :span="10">
<div class="flex" :gutter="10">
<div class="flex-1">
<el-card>
<div slot="header" class="header">
<el-select v-model="queryParams.typeId" placeholder="选择类型" style="width:120px" clearable>
<el-select v-model="queryParams.typeId" :placeholder="$t('选择类型')" style="width:120px" clearable>
<el-option v-for="item in typeList" :key="item.id" :label="item.titleZh" :value="item.id" />
</el-select>
<el-select v-model="queryParams.attrId" placeholder="选择属性" style="width:120px" clearable>
<el-select v-model="queryParams.attrId" :placeholder="$t('选择属性')" style="width:120px" clearable>
<el-option v-for="item in attrList" :key="item.id" :label="item.attrName" :value="item.id" />
</el-select>
<el-input v-model="queryParams.titleZh" placeholder="产品关键字" style="width:120px" clearable />
<el-button type="primary" @click="reLoad">搜索</el-button>
<el-input v-model="queryParams.titleZh" :placeholder="$t('产品关键字')" style="width:120px" clearable />
<el-button type="primary" @click="reLoad">{{$t('搜索')}}</el-button>
</div>
<div class="list">
<div class="item" v-for="item in list" :key="item.id">
......@@ -19,11 +19,11 @@
</div>
</div>
</el-card>
</el-col>
<el-col :span="10">
<el-card>
</div>
<div class="flex-1 ml-10">
<el-card style="height:100%">
<div slot="header" class="header">
已选产品
{{$t('已选产品')}}
</div>
<div class="list">
<div class="item" v-for="(choosed) in choosedList" :key="choosed.id" :data-data="JSON.stringify(choosed)">
......@@ -32,8 +32,8 @@
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import { getProductPage } from '@/api/ecw/product'
......@@ -99,7 +99,7 @@ export default {
},
loadNextPage() {
if (this.page >= this.pages) {
return this.$message.error('已加载全部')
return this.$message.error(this.$t('已加载全部'))
}
this.queryParams.page++
this.getList()
......
......@@ -55,11 +55,12 @@
import {createCustomer} from '@/api/ecw/customer'
import {listServiceUser} from "@/api/system/user"
import { getCountryListAll } from '@/api/ecw/country'
import {getCustomerContactsSelect} from '@/api/ecw/customerContacts'
export default {
name: "quickCreateCustomer",
props:{
default: Object,
type: String,
},
data(){
......@@ -93,8 +94,8 @@ export default {
}
},
created() {
if(this.default){
this.$set(this, 'form', Object.assign({}, this.default, this.form))
if(this.type){
this.$set(this.form, 'type', this.type)
}
listServiceUser().then(r => {
this.serviceUserList = r.data
......@@ -120,9 +121,10 @@ export default {
// 添加的提交
createCustomer(this.form).then(res => {
this.$modal.msgSuccess("新增成功");
this.$emit('success', res.data)
// this.getList();
});
return getCustomerContactsSelect({ids: res.data})
}).then(res => {
this.$emit('success', res.data[0])
})
});
},
}
......
<template>
<div>
<div class="filters mb-10">
运输方式
<dict-selector :type='DICT_TYPE.ECW_TRANSPORT_TYPE' v-model="transportType" placeholder="请选择运输方式" style="width:150px" />
{{$t('运输方式')}}
<dict-selector :type='DICT_TYPE.ECW_TRANSPORT_TYPE' v-model="transportType" :placeholder="$t('请选择运输方式')" style="width:150px" />
始发地
<el-select placeholder="请选择始发地" v-model="exportCity" clearable>
{{$t('始发地')}}
<el-select :placeholder="$t('请选择始发地')" v-model="exportCity" clearable>
<el-option v-for="item in exportCityList" :key="item.id" :label="item.titleZh" :value="item.id" />
</el-select>
目的地
<el-select placeholder="请选择目的地" v-model="importCity" clearable>
{{$t('目的地')}}
<el-select :placeholder="$t('请选择目的地')" v-model="importCity" clearable>
<el-option v-for="item in importCityList" :key="item.id" :label="item.titleZh" :value="item.id" />
</el-select>
出货渠道:
<el-select placeholder="请选择目渠道" v-model="channelId" clearable>
{{$t('出货渠道')}}:
<el-select :placeholder="$t('请选择目渠道')" v-model="channelId" clearable>
<el-option v-for="item in channelList" :key="item.channelId" :label="item.nameZh" :value="item.channelId" />
</el-select>
</div>
<div class="mb-10">
<el-radio-group v-model="checkAll">
<el-radio :label="true">全选</el-radio>
<el-radio :label="false">全不选</el-radio>
<el-radio :label="true">{{$t('全选')}}</el-radio>
<el-radio :label="false">{{$t('全不选')}}</el-radio>
</el-radio-group>
</div>
<el-row class="" :gutter="10">
......@@ -30,19 +30,27 @@
<el-col :span="12" :key="item.value">
<el-card class="mb-10">
<div slot="header">
{{item.label}}
<el-link type="primary" @click.native="toggleHide(item.value)" style="float:right">{{item._hide ? '展开' : '折叠'}}</el-link>
{{$l(item, 'label')}}
<el-link type="primary" @click.native="toggleHide(item.value)" style="float:right">{{item._hide ? $t('展开') : $t('折叠')}}</el-link>
</div>
<!--table需要给一个key,否则全选的时候不会自动更新渲染-->
<el-table v-if="!hideMap[item.value]" :data="item.routerList" :span-method="SpanMethod" border :key="selectedRoutes.length + item.value">
<el-table-column label="始发地" prop="startTitleZh" />
<el-table-column label="目的地" prop="destTitleZh" />
<el-table-column label="渠道" prop="startTitleZh">
<el-table-column :label="$t('始发地')" prop="startTitleZh">
<template slot-scope="{row}">
{{row.channel.nameZh}}
{{$l(row, 'startTitle')}}
</template>
</el-table-column>
<el-table-column label="操作" prop="">
<el-table-column :label="$t('目的地')" prop="destTitleZh" >
<template slot-scope="{row}">
{{$l(row, 'destTitle')}}
</template>
</el-table-column>
<el-table-column :label="$t('渠道')" prop="startTitleZh">
<template slot-scope="{row}">
{{$l(row.channel, 'name')}}
</template>
</el-table-column>
<el-table-column :label="$t('操作')" prop="">
<template slot-scope="{row}">
<el-checkbox :checked="getSelectedIndex(row) > -1" @change="toggleChecker(row, $event)"></el-checkbox>
</template>
......@@ -130,6 +138,7 @@ export default {
let child = {
label: item.label,
labelEn: item.labelEn,
value: item.value,
_hide: false, // 是否折叠
routerList: routerList
......
......@@ -5,7 +5,7 @@
<el-button size="mini" type="primary" icon="el-icon-plus" @click="listData.push({})"></el-button>
</div>
<el-table :data="listData" border>
<el-table-column label="序号" width="90px">
<el-table-column :label="$t('序号')" width="90px">
<template slot-scope="scope">
{{scope.$index + 1}}
</template>
......@@ -22,7 +22,7 @@
<el-input v-model="row.job" />
</template>
</el-table-column> -->
<el-table-column label="操作">
<el-table-column :label="$t('操作')">
<template slot-scope="scope">
<el-button type="danger" size="mini" icon="el-icon-delete" @click="del(scope.$index)"></el-button>
</template>
......@@ -66,7 +66,7 @@ export default {
},
methods:{
del(index){
this.$confirm('确定删除此行?')
this.$confirm(this.$t('确定删除此行?'))
.then(res => {
this.listData.splice(index, 1)
})
......
......@@ -11,8 +11,8 @@
/>
</div>
<el-form label-position="left" label-width="100px">
<el-form-item label="抄送">
<el-select v-model="valueSync" multiple placeholder="请选择抄送人" style="width:100%">
<el-form-item :label="$t('抄送')">
<el-select v-model="valueSync" multiple :placeholder="$t('请选择抄送人')" style="width:100%">
<el-option
v-for="item in users"
:key="item.id"
......
import Vue from 'vue'
import VueI18n from './vue-i18n/vue-i18n.common'
import {getLocale} from '@/utils/db'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: getLocale() || 'zh_CN',
formatFallbackMessages: true,
messages: {
'en_US': require('./languages/en_US.json'),
'zh_CN': require('./languages/zh_CN.json')
}
})
console.log({i18n})
Vue.prototype.$l = (object, field) => {
let prefix = i18n.locale.split('_')[0]
let append = prefix.charAt(0).toUpperCase() + prefix.toLowerCase().substr(1)
// 如果object是null则返回字段名
if(!object) return field + append
return object[field + append] || object[field]
}
/* Vue.filter('$t', Vue.$i18n)
*/
// 重新console.warn来捕获未翻译的内容
/* console.wareOrig = console.warn
console.warn = (...data) => {
console.log('warn', typeof data)
console.wareOrig(data)
} */
export default i18n
\ No newline at end of file
{
"登录": "Login",
"个人中心": "User Center",
"布局设置": "Layout",
"编号": "No",
"新增": "Create",
"搜索": "Search",
"重置": "Reset",
"新建订单": "New Order"
}
\ No newline at end of file
{
"登录": "登录"
}
\ No newline at end of file
This diff is collapsed.
The MIT License (MIT)
Copyright (c) 2016 kazuya kawaguchi
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<p align="center"><img width="128px" height="112px" src="./assets/vue-i18n-logo.png" alt="Vue I18n logo"></p>
<h1 align="center">vue-i18n</h1>
<p align="center">
<a href="https://circleci.com/gh/kazupon/vue-i18n/tree/dev"><img src="https://circleci.com/gh/kazupon/vue-i18n/tree/dev.svg?style=shield" alt="Build Status"></a>
<a href="http://badge.fury.io/js/vue-i18n"><img src="https://badge.fury.io/js/vue-i18n.svg" alt="NPM version"></a>
<a href="https://discord.gg/4yCnk2m"><img src="https://img.shields.io/badge/Discord-join%20chat-738bd7.svg" alt="vue-i18n channel on Discord"></a>
</p>
<p align="center">Internationalization plugin for Vue.js</p>
<br/>
<h3 align="center">🏅 Platinum Sponsors</h3>
<p align="center">
<a href="https://zenarchitects.co.jp/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/zenarchitects.png"
width="400px"
/>
</a>
</p>
<h3 align="center">✨ Special Sponsors</h3>
<p align="center">
<a
href="https://plaid.co.jp/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/plaid.svg"
width="240px"
/>
</a>
</p>
<h3 align="center">🥇 Gold Sponsors</h3>
<p align="center">
<a
href="https://nuxtjs.org/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/nuxt.png"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="https://rapidapi.com/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/RapidAPI.svg"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="https://localazy.com/blog/how-to-localize-vuejs-app-with-vue-i18n-and-localazy?utm_source=kazupon&utm_medium=banner&utm_campaign=sponsorships_kazupon&utm_content=logo"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/localazy.svg"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="https://crowdin.com/teams/engineering?utm_source=vue-i18n.intlify.dev&utm_medium=referral"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/crowdin.svg"
width="240px"
/>
</a>
</p>
<h3 align="center">🥈 Silver Sponsors</h3>
<h3 align="center">🥉 Bronze Sponsors</h3>
<p align="center">
<a href="https://www.sendcloud.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/sendcloud.png"
width="144px"
/>
</a>
</p>
<p align="center">
<a href="https://www.vuemastery.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/vuemastery.png"
width="144px"
/>
</a>
</p>
<p align="center">
<a href="https://www.deci-bel.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/decibel.png"
width="144px"
/>
</a>
</p>
<br/>
## ⚠️ NOTICE
**This repository is for Vue I18n v8.x and Vue 2**
**If you want to know about how to usage for Vue I18n v9 on Vue 3, See the [this repository](https://github.com/intlify/vue-i18n-next))**
## 🙋‍♂️ About support for v8
We will follow Vue v2 maintenance lifespan
## 📔 Documentation
About Vue I18n v8.x, See [here](http://kazupon.github.io/vue-i18n/)
If you want to read Vue I18n v9 docs, See [here](https://vue-i18n.intlify.dev/)
## 📜 Changelog
Detailed changes for each release are documented in the [CHANGELOG.md](https://github.com/kazupon/vue-i18n/blob/dev/CHANGELOG.md).
## ❗ Issues
Please make sure to read the [Issue Reporting Checklist](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
## 💪 Contribution
Please make sure to read the [Contributing Guide](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md) before making a pull request.
## ©️ License
[MIT](http://opensource.org/licenses/MIT)
declare var Intl: any;
declare type Path = string;
declare type Locale = string;
declare type MessageContext = {
list: (index: number) => mixed,
named: (key: string) => mixed,
linked: (key: string) => TranslateResult,
values: any,
path: string,
formatter: Formatter,
messages: LocaleMessages,
locale: Locale
}
declare type MessageFunction = (ctx: MessageContext) => string
declare type FallbackLocale = string | string[] | false | { [locale: string]: string[] };
declare type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray;
declare type LocaleMessageObject = { [key: Path]: LocaleMessage };
declare type LocaleMessageArray = Array<LocaleMessage>;
declare type LocaleMessages = { [key: Locale]: LocaleMessageObject };
// This options is the same as Intl.DateTimeFormat constructor options:
// http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor
declare type DateTimeFormatOptions = {
year?: 'numeric' | '2-digit',
month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long',
day?: 'numeric' | '2-digit',
hour?: 'numeric' | '2-digit',
minute?: 'numeric' | '2-digit',
second?: 'numeric' | '2-digit',
weekday?: 'narrow' | 'short' | 'long',
hour12?: boolean,
era?: 'narrow' | 'short' | 'long',
timeZone?: string, // IANA time zone
timeZoneName?: 'short' | 'long',
localeMatcher?: 'lookup' | 'best fit',
formatMatcher?: 'basic' | 'best fit'
};
declare type DateTimeFormat = { [key: string]: DateTimeFormatOptions };
declare type DateTimeFormats = { [key: Locale]: DateTimeFormat };
// This options is the same as Intl.NumberFormat constructor options:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
declare type NumberFormatOptions = {
style?: 'decimal' | 'currency' | 'percent',
currency?: string, // ISO 4217 currency codes
currencyDisplay?: 'symbol' | 'code' | 'name',
useGrouping?: boolean,
minimumIntegerDigits?: number,
minimumFractionDigits?: number,
maximumFractionDigits?: number,
minimumSignificantDigits?: number,
maximumSignificantDigits?: number,
localeMatcher?: 'lookup' | 'best fit',
formatMatcher?: 'basic' | 'best fit'
};
declare type NumberFormat = { [key: string]: NumberFormatOptions };
declare type NumberFormats = { [key: Locale]: NumberFormat };
declare type Modifiers = { [key: string]: (str: string) => string };
declare type TranslateResult = string | LocaleMessages;
declare type DateTimeFormatResult = string;
declare type NumberFormatResult = string;
declare type MissingHandler = (locale: Locale, key: Path, vm?: any) => string | void;
declare type PostTranslationHandler = (str: string, key?: string) => string;
declare type GetChoiceIndex = (choice: number, choicesLength: number) => number
declare type ComponentInstanceCreatedListener = (newI18n: I18n, rootI18n: I18n) => void;
declare type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign';
declare type FormattedNumberPart = {
type: FormattedNumberPartType,
value: string,
};
// This array is the same as Intl.NumberFormat.formatToParts() return value:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts#Return_value
declare type NumberFormatToPartsResult = Array<FormattedNumberPart>;
declare type WarnHtmlInMessageLevel = 'off' | 'warn' | 'error';
declare type I18nOptions = {
locale?: Locale,
fallbackLocale?: FallbackLocale,
messages?: LocaleMessages,
dateTimeFormats?: DateTimeFormats,
datetimeFormats?: DateTimeFormats,
numberFormats?: NumberFormats,
formatter?: Formatter,
missing?: MissingHandler,
modifiers?: Modifiers,
root?: I18n, // for internal
fallbackRoot?: boolean,
fallbackRootWithEmptyString?: boolean,
formatFallbackMessages?: boolean,
sync?: boolean,
silentTranslationWarn?: boolean | RegExp,
silentFallbackWarn?: boolean | RegExp,
pluralizationRules?: PluralizationRules,
preserveDirectiveContent?: boolean,
warnHtmlInMessage?: WarnHtmlInMessageLevel,
sharedMessages?: LocaleMessage,
postTranslation?: PostTranslationHandler,
componentInstanceCreatedListener?: ComponentInstanceCreatedListener,
escapeParameterHtml?: boolean,
__VUE_I18N_BRIDGE__?: string,
};
declare type IntlAvailability = {
dateTimeFormat: boolean,
numberFormat: boolean
};
declare type PluralizationRules = {
[lang: string]: GetChoiceIndex,
}
declare interface I18n {
static install: () => void, // for Vue plugin interface
static version: string,
static availabilities: IntlAvailability,
get vm (): any, // for internal
get locale (): Locale,
set locale (locale: Locale): void,
get fallbackLocale (): FallbackLocale,
set fallbackLocale (locale: FallbackLocale): void,
get messages (): LocaleMessages,
get dateTimeFormats (): DateTimeFormats,
get numberFormats (): NumberFormats,
get availableLocales (): Locale[],
get missing (): ?MissingHandler,
set missing (handler: MissingHandler): void,
get formatter (): Formatter,
set formatter (formatter: Formatter): void,
get formatFallbackMessages (): boolean,
set formatFallbackMessages (fallback: boolean): void,
get silentTranslationWarn (): boolean | RegExp,
set silentTranslationWarn (silent: boolean | RegExp): void,
get silentFallbackWarn (): boolean | RegExp,
set silentFallbackWarn (slient: boolean | RegExp): void,
get pluralizationRules (): PluralizationRules,
set pluralizationRules (rules: PluralizationRules): void,
get preserveDirectiveContent (): boolean,
set preserveDirectiveContent (preserve: boolean): void,
get warnHtmlInMessage (): WarnHtmlInMessageLevel,
set warnHtmlInMessage (level: WarnHtmlInMessageLevel): void,
get postTranslation (): ?PostTranslationHandler,
set postTranslation (handler: PostTranslationHandler): void,
getLocaleMessage (locale: Locale): LocaleMessageObject,
setLocaleMessage (locale: Locale, message: LocaleMessageObject): void,
mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void,
t (key: Path, ...values: any): TranslateResult,
i (key: Path, locale: Locale, values: Object): TranslateResult,
tc (key: Path, choice?: number, ...values: any): TranslateResult,
te (key: Path, locale?: Locale): boolean,
getDateTimeFormat (locale: Locale): DateTimeFormat,
setDateTimeFormat (locale: Locale, format: DateTimeFormat): void,
mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void,
d (value: number | Date, ...args: any): DateTimeFormatResult,
getNumberFormat (locale: Locale): NumberFormat,
setNumberFormat (locale: Locale, format: NumberFormat): void,
mergeNumberFormat (locale: Locale, format: NumberFormat): void,
n (value: number, ...args: any): NumberFormatResult,
getChoiceIndex: GetChoiceIndex,
pluralizationRules: PluralizationRules,
preserveDirectiveContent: boolean
};
declare interface Formatter {
interpolate (message: string, values: any, path: string): (Array<any> | null)
};
declare type $npm$Vue$Dictionaly<T> = { [key: string]: T }
declare type Util = {
extend: (to: Object, from: ?Object) => Object,
hasOwn: (obj: Object, key: string) => boolean,
isPlainObject: (obj: any) => boolean,
isObject: (obj: mixed) => boolean,
}
declare type Config = {
optionMergeStrategies: $npm$Vue$Dictionaly<Function>,
silent: boolean,
productionTip: boolean,
performance: boolean,
devtools: boolean,
errorHandler: ?(err: Error, vm: Vue, info: string) => void,
ignoredElements: Array<string>,
keyCodes: $npm$Vue$Dictionaly<number>,
isReservedTag: (x?: string) => boolean,
parsePlatformTagName: (x: string) => string,
isUnknownElement: (x?: string) => boolean,
getTagNamespace: (x?: string) => string | void,
mustUseProp: (tag: string, type: ?string, name: string) => boolean,
}
declare interface Vue {
static config: Config,
static util: Util,
static version: string,
}
{
"_from": "vue-i18n@^8.27.2",
"_id": "vue-i18n@8.27.2",
"_inBundle": false,
"_integrity": "sha512-QVzn7u2WVH8F7eSKIM00lujC7x1mnuGPaTnDTmB01Hd709jDtB9kYtBqM+MWmp5AJRx3gnqAdZbee9MelqwFBg==",
"_location": "/vue-i18n",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "vue-i18n@^8.27.2",
"name": "vue-i18n",
"escapedName": "vue-i18n",
"rawSpec": "^8.27.2",
"saveSpec": null,
"fetchSpec": "^8.27.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.27.2.tgz",
"_shasum": "b649a65ff42b7d1a482679b732902f889965a068",
"_spec": "vue-i18n@^8.27.2",
"_where": "D:\\projects\\jiedao-admin",
"author": {
"name": "kazuya kawaguchi",
"email": "kawakazu80@gmail.com"
},
"bugs": {
"url": "https://github.com/kazupon/vue-i18n/issues"
},
"bundleDependencies": false,
"changelog": {
"labels": {
"Type: Feature": ":star: New Features",
"Type: Bug": ":bug: Bug Fixes",
"Type: Security": ":lock: Security Fixes",
"Type: Performance": ":chart_with_upwards_trend: Performance Fixes",
"Type: Improvement": ":zap: Improved Features",
"Type: Breaking": ":boom: Breaking Change",
"Type: Deprecated": ":warning: Deprecated Features",
"Type: I18n": ":globe_with_meridians: Internationalization",
"Type: A11y": ":wheelchair: Accessibility",
"Type: Documentation": ":pencil: Documentation"
}
},
"deprecated": false,
"description": "Internationalization plugin for Vue.js",
"devDependencies": {
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-syntax-flow": "^7.0.0",
"@babel/plugin-transform-flow-strip-types": "^7.0.0",
"@typescript-eslint/eslint-plugin": "^3.0.0",
"@typescript-eslint/parser": "^3.0.0",
"@vue/babel-preset-app": "^4.4.1",
"@vuepress/theme-vue": "^1.9.7",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.0.0",
"babel-preset-power-assert": "^3.0.0",
"buble": "^0.19.3",
"chromedriver": "^102.0.0",
"core-js": "^3.6.5",
"cross-env": "^7.0.2",
"cross-spawn": "^7.0.3",
"eslint": "^6.8.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-flowtype": "^4.7.0",
"eslint-plugin-ie11": "^1.0.0",
"eslint-plugin-no-autofix": "^1.0.1",
"eslint-plugin-vue": "^7.14.0",
"eslint-plugin-vue-libs": "^4.0.0",
"flow-bin": "^0.38.0",
"http-server": "^0.12.3",
"intl": "^1.2.5",
"karma": "^5.0.9",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.2",
"karma-firefox-launcher": "^2.1.1",
"karma-mocha": "^2.0.1",
"karma-mocha-reporter": "^2.2.5",
"karma-safari-launcher": "^1.0.0",
"karma-sauce-launcher": "^4.1.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.2",
"lerna-changelog": "^1.0.0",
"lerna-changelog-label-schema": "^3.0.0",
"mocha": "^7.2.0",
"mocha-loader": "^5.0.0",
"nightwatch": "^1.3.5",
"nightwatch-helpers": "^1.2.0",
"power-assert": "^1.6.0",
"rollup": "^0.66.0",
"rollup-plugin-buble": "^0.19.2",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-flow-no-whitespace": "^1.0.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0",
"selenium-server": "^3.141.59",
"shipjs": "^0.23.3",
"sinon": "^11.1.1",
"terser": "^3.17.0",
"typescript": "^3.9.3",
"vue": "^2.5.17",
"vue-github-button": "^1.1.2",
"vue-template-compiler": "^2.5.17",
"vuepress": "^1.8.2",
"webpack": "^4.43.0",
"webpack-cli": "^4.7.2",
"webpack-dev-middleware": "^5.0.0",
"webpack-dev-server": "^3.11.0"
},
"files": [
"dist/vue-i18n.js",
"dist/vue-i18n.min.js",
"dist/vue-i18n.common.js",
"dist/vue-i18n.esm.js",
"dist/vue-i18n.esm.browser.js",
"dist/vue-i18n.esm.browser.min.js",
"src/**/*.js",
"types/*.d.ts",
"decls",
"vetur/tags.json",
"vetur/attributes.json"
],
"homepage": "https://github.com/kazupon/vue-i18n#readme",
"keywords": [
"i18n",
"internationalization",
"plugin",
"vue",
"vue.js"
],
"license": "MIT",
"main": "dist/vue-i18n.common.js",
"module": "dist/vue-i18n.esm.js",
"name": "vue-i18n",
"repository": {
"type": "git",
"url": "git+https://github.com/kazupon/vue-i18n.git"
},
"scripts": {
"build": "node config/build.js",
"clean": "rm -rf coverage && rm -rf dist/*.js* && rm ./*.log",
"coverage": "cat ./coverage/lcov.info",
"dev": "cross-env BABEL_ENV=test webpack-dev-server --inline --hot --open --content-base ./test/unit/ --config config/webpack.dev.conf.js",
"docs:build": "cross-env NODE_ENV=production node config/version.js && cross-env NODE_ENV=production vuepress build vuepress -d docs",
"docs:clean": "rm -rf docs/**",
"docs:dev": "vuepress dev vuepress",
"flow": "flow check",
"lint": "eslint --fix src test types/**/*.ts",
"release:prepare": "shipjs prepare",
"release:trigger": "shipjs trigger",
"sauce": "npm run sauce:coolkids && npm run sauce:ie && npm run sauce:mobile",
"sauce:coolkids": "karma start config/karma.sauce.conf.js -- 0",
"sauce:ie": "karma start config/karma.sauce.conf.js -- 1",
"sauce:mobile": "karma start config/karma.sauce.conf.js -- 2",
"test": "npm run lint && npm run flow && npm run test:types && npm run test:cover && npm run test:e2e",
"test:cover": "cross-env BABEL_ENV=test karma start config/karma.cover.conf.js",
"test:e2e": "npm run build && node test/e2e/runner.js",
"test:types": "tsc -p types",
"test:unit": "cross-env BABEL_ENV=test karma start config/karma.unit.conf.js",
"test:unit:ci": "cross-env BABEL_ENV=test karma start config/karma.unit.ci.conf.js"
},
"sideEffects": false,
"types": "types/index.d.ts",
"unpkg": "dist/vue-i18n.js",
"version": "8.27.2",
"vetur": {
"tags": "vetur/tags.json",
"attributes": "vetur/attributes.json"
}
}
/* @flow */
import { warn } from '../util'
export default {
name: 'i18n',
functional: true,
props: {
tag: {
type: [String, Boolean, Object],
default: 'span'
},
path: {
type: String,
required: true
},
locale: {
type: String
},
places: {
type: [Array, Object]
}
},
render (h: Function, { data, parent, props, slots }: Object) {
const { $i18n } = parent
if (!$i18n) {
if (process.env.NODE_ENV !== 'production') {
warn('Cannot find VueI18n instance!')
}
return
}
const { path, locale, places } = props
const params = slots()
const children = $i18n.i(
path,
locale,
onlyHasDefaultPlace(params) || places
? useLegacyPlaces(params.default, places)
: params
)
const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span'
return tag ? h(tag, data, children) : children
}
}
function onlyHasDefaultPlace (params) {
let prop
for (prop in params) {
if (prop !== 'default') { return false }
}
return Boolean(prop)
}
function useLegacyPlaces (children, places) {
const params = places ? createParamsFromPlaces(places) : {}
if (!children) { return params }
// Filter empty text nodes
children = children.filter(child => {
return child.tag || child.text.trim() !== ''
})
const everyPlace = children.every(vnodeHasPlaceAttribute)
if (process.env.NODE_ENV !== 'production' && everyPlace) {
warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.')
}
return children.reduce(
everyPlace ? assignChildPlace : assignChildIndex,
params
)
}
function createParamsFromPlaces (places) {
if (process.env.NODE_ENV !== 'production') {
warn('`places` prop is deprecated in next major version. Please switch to Vue slots.')
}
return Array.isArray(places)
? places.reduce(assignChildIndex, {})
: Object.assign({}, places)
}
function assignChildPlace (params, child) {
if (child.data && child.data.attrs && child.data.attrs.place) {
params[child.data.attrs.place] = child
}
return params
}
function assignChildIndex (params, child, index) {
params[index] = child
return params
}
function vnodeHasPlaceAttribute (vnode) {
return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
}
/* @flow */
import { warn, isString, isObject, includes, numberFormatKeys } from '../util'
export default {
name: 'i18n-n',
functional: true,
props: {
tag: {
type: [String, Boolean, Object],
default: 'span'
},
value: {
type: Number,
required: true
},
format: {
type: [String, Object]
},
locale: {
type: String
}
},
render (h: Function, { props, parent, data }: Object) {
const i18n = parent.$i18n
if (!i18n) {
if (process.env.NODE_ENV !== 'production') {
warn('Cannot find VueI18n instance!')
}
return null
}
let key: ?string = null
let options: ?NumberFormatOptions = null
if (isString(props.format)) {
key = props.format
} else if (isObject(props.format)) {
if (props.format.key) {
key = props.format.key
}
// Filter out number format options only
options = Object.keys(props.format).reduce((acc, prop) => {
if (includes(numberFormatKeys, prop)) {
return Object.assign({}, acc, { [prop]: props.format[prop] })
}
return acc
}, null)
}
const locale: Locale = props.locale || i18n.locale
const parts: NumberFormatToPartsResult = i18n._ntp(props.value, locale, key, options)
const values = parts.map((part, index) => {
const slot: ?Function = data.scopedSlots && data.scopedSlots[part.type]
return slot ? slot({ [part.type]: part.value, index, parts }) : part.value
})
const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span'
return tag
? h(tag, {
attrs: data.attrs,
'class': data['class'],
staticClass: data.staticClass
}, values)
: values
}
}
/* @flow */
import { warn, isString, isPlainObject, looseEqual } from './util'
export function bind (el: any, binding: Object, vnode: any): void {
if (!assert(el, vnode)) { return }
t(el, binding, vnode)
}
export function update (el: any, binding: Object, vnode: any, oldVNode: any): void {
if (!assert(el, vnode)) { return }
const i18n: any = vnode.context.$i18n
if (localeEqual(el, vnode) &&
(looseEqual(binding.value, binding.oldValue) &&
looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
t(el, binding, vnode)
}
export function unbind (el: any, binding: Object, vnode: any, oldVNode: any): void {
const vm: any = vnode.context
if (!vm) {
warn('Vue instance does not exists in VNode context')
return
}
const i18n: any = vnode.context.$i18n || {}
if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
el.textContent = ''
}
el._vt = undefined
delete el['_vt']
el._locale = undefined
delete el['_locale']
el._localeMessage = undefined
delete el['_localeMessage']
}
function assert (el: any, vnode: any): boolean {
const vm: any = vnode.context
if (!vm) {
warn('Vue instance does not exists in VNode context')
return false
}
if (!vm.$i18n) {
warn('VueI18n instance does not exists in Vue instance')
return false
}
return true
}
function localeEqual (el: any, vnode: any): boolean {
const vm: any = vnode.context
return el._locale === vm.$i18n.locale
}
function t (el: any, binding: Object, vnode: any): void {
const value: any = binding.value
const { path, locale, args, choice } = parseValue(value)
if (!path && !locale && !args) {
warn('value type not supported')
return
}
if (!path) {
warn('`path` is required in v-t directive')
return
}
const vm: any = vnode.context
if (choice != null) {
el._vt = el.textContent = vm.$i18n.tc(path, choice, ...makeParams(locale, args))
} else {
el._vt = el.textContent = vm.$i18n.t(path, ...makeParams(locale, args))
}
el._locale = vm.$i18n.locale
el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale)
}
function parseValue (value: any): Object {
let path: ?string
let locale: ?Locale
let args: any
let choice: ?number
if (isString(value)) {
path = value
} else if (isPlainObject(value)) {
path = value.path
locale = value.locale
args = value.args
choice = value.choice
}
return { path, locale, args, choice }
}
function makeParams (locale: Locale, args: any): Array<any> {
const params: Array<any> = []
locale && params.push(locale)
if (args && (Array.isArray(args) || isPlainObject(args))) {
params.push(args)
}
return params
}
/* @flow */
export default function extend (Vue: any): void {
if (!Vue.prototype.hasOwnProperty('$i18n')) {
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$i18n', {
get () { return this._i18n }
})
}
Vue.prototype.$t = function (key: Path, ...values: any): TranslateResult {
const i18n = this.$i18n
return i18n._t(key, i18n.locale, i18n._getMessages(), this, ...values)
}
Vue.prototype.$tc = function (key: Path, choice?: number, ...values: any): TranslateResult {
const i18n = this.$i18n
return i18n._tc(key, i18n.locale, i18n._getMessages(), this, choice, ...values)
}
Vue.prototype.$te = function (key: Path, locale?: Locale): boolean {
const i18n = this.$i18n
return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
}
Vue.prototype.$d = function (value: number | Date, ...args: any): DateTimeFormatResult {
return this.$i18n.d(value, ...args)
}
Vue.prototype.$n = function (value: number, ...args: any): NumberFormatResult {
return this.$i18n.n(value, ...args)
}
}
/* @flow */
import { warn, isObject } from './util'
export default class BaseFormatter {
_caches: { [key: string]: Array<Token> }
constructor () {
this._caches = Object.create(null)
}
interpolate (message: string, values: any): Array<any> {
if (!values) {
return [message]
}
let tokens: Array<Token> = this._caches[message]
if (!tokens) {
tokens = parse(message)
this._caches[message] = tokens
}
return compile(tokens, values)
}
}
type Token = {
type: 'text' | 'named' | 'list' | 'unknown',
value: string
}
const RE_TOKEN_LIST_VALUE: RegExp = /^(?:\d)+/
const RE_TOKEN_NAMED_VALUE: RegExp = /^(?:\w)+/
export function parse (format: string): Array<Token> {
const tokens: Array<Token> = []
let position: number = 0
let text: string = ''
while (position < format.length) {
let char: string = format[position++]
if (char === '{') {
if (text) {
tokens.push({ type: 'text', value: text })
}
text = ''
let sub: string = ''
char = format[position++]
while (char !== undefined && char !== '}') {
sub += char
char = format[position++]
}
const isClosed = char === '}'
const type = RE_TOKEN_LIST_VALUE.test(sub)
? 'list'
: isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
? 'named'
: 'unknown'
tokens.push({ value: sub, type })
} else if (char === '%') {
// when found rails i18n syntax, skip text capture
if (format[(position)] !== '{') {
text += char
}
} else {
text += char
}
}
text && tokens.push({ type: 'text', value: text })
return tokens
}
export function compile (tokens: Array<Token>, values: Object | Array<any>): Array<any> {
const compiled: Array<any> = []
let index: number = 0
const mode: string = Array.isArray(values)
? 'list'
: isObject(values)
? 'named'
: 'unknown'
if (mode === 'unknown') { return compiled }
while (index < tokens.length) {
const token: Token = tokens[index]
switch (token.type) {
case 'text':
compiled.push(token.value)
break
case 'list':
compiled.push(values[parseInt(token.value, 10)])
break
case 'named':
if (mode === 'named') {
compiled.push((values: any)[token.value])
} else {
if (process.env.NODE_ENV !== 'production') {
warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`)
}
}
break
case 'unknown':
if (process.env.NODE_ENV !== 'production') {
warn(`Detect 'unknown' type of token!`)
}
break
}
index++
}
return compiled
}
This diff is collapsed.
import { warn } from './util'
import extend from './extend'
import defineMixin from './mixin'
import interpolationComponent from './components/interpolation'
import numberComponent from './components/number'
import { bind, update, unbind } from './directive'
export let Vue
export function install (_Vue, options = { bridge: false }) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) {
warn('already installed.')
return
}
install.installed = true
Vue = _Vue
const version = (Vue.version && Number(Vue.version.split('.')[0])) || -1
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && version < 2) {
warn(`vue-i18n (${install.version}) need to use Vue 2.0 or later (Vue: ${Vue.version}).`)
return
}
extend(Vue)
Vue.mixin(defineMixin(options.bridge))
Vue.directive('t', { bind, update, unbind })
Vue.component(interpolationComponent.name, interpolationComponent)
Vue.component(numberComponent.name, numberComponent)
// use simple mergeStrategies to prevent i18n instance lose '__proto__'
const strats = Vue.config.optionMergeStrategies
strats.i18n = function (parentVal, childVal) {
return childVal === undefined
? parentVal
: childVal
}
}
/* @flow */
import VueI18n from './index'
import { isPlainObject, warn, error, merge } from './util'
/**
* Mixin
*
* If `bridge` mode, empty mixin is returned,
* else regulary mixin implementation is returned.
*/
export default function defineMixin (bridge: boolean = false) {
function mounted (): void {
if (this !== this.$root && this.$options.__INTLIFY_META__ && this.$el) {
this.$el.setAttribute('data-intlify', this.$options.__INTLIFY_META__)
}
}
return bridge
? { mounted } // delegate `vue-i18n-bridge` mixin implementation
: { // regulary
beforeCreate (): void {
const options: any = this.$options
options.i18n = options.i18n || ((options.__i18nBridge || options.__i18n) ? {} : null)
if (options.i18n) {
if (options.i18n instanceof VueI18n) {
// init locale messages via custom blocks
if ((options.__i18nBridge || options.__i18n)) {
try {
let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {}
const __i18n = options.__i18nBridge || options.__i18n
__i18n.forEach(resource => {
localeMessages = merge(localeMessages, JSON.parse(resource))
})
Object.keys(localeMessages).forEach((locale: Locale) => {
options.i18n.mergeLocaleMessage(locale, localeMessages[locale])
})
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
error(`Cannot parse locale messages via custom blocks.`, e)
}
}
}
this._i18n = options.i18n
this._i18nWatcher = this._i18n.watchI18nData()
} else if (isPlainObject(options.i18n)) {
const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
? this.$root.$i18n
: null
// component local i18n
if (rootI18n) {
options.i18n.root = this.$root
options.i18n.formatter = rootI18n.formatter
options.i18n.fallbackLocale = rootI18n.fallbackLocale
options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages
options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn
options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn
options.i18n.pluralizationRules = rootI18n.pluralizationRules
options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent
}
// init locale messages via custom blocks
if ((options.__i18nBridge || options.__i18n)) {
try {
let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {}
const __i18n = options.__i18nBridge || options.__i18n
__i18n.forEach(resource => {
localeMessages = merge(localeMessages, JSON.parse(resource))
})
options.i18n.messages = localeMessages
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot parse locale messages via custom blocks.`, e)
}
}
}
const { sharedMessages } = options.i18n
if (sharedMessages && isPlainObject(sharedMessages)) {
options.i18n.messages = merge(options.i18n.messages, sharedMessages)
}
this._i18n = new VueI18n(options.i18n)
this._i18nWatcher = this._i18n.watchI18nData()
if (options.i18n.sync === undefined || !!options.i18n.sync) {
this._localeWatcher = this.$i18n.watchLocale()
}
if (rootI18n) {
rootI18n.onComponentInstanceCreated(this._i18n)
}
} else {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot be interpreted 'i18n' option.`)
}
}
} else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
// root i18n
this._i18n = this.$root.$i18n
} else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
// parent i18n
this._i18n = options.parent.$i18n
}
},
beforeMount (): void {
const options: any = this.$options
options.i18n = options.i18n || ((options.__i18nBridge || options.__i18n) ? {} : null)
if (options.i18n) {
if (options.i18n instanceof VueI18n) {
// init locale messages via custom blocks
this._i18n.subscribeDataChanging(this)
this._subscribing = true
} else if (isPlainObject(options.i18n)) {
this._i18n.subscribeDataChanging(this)
this._subscribing = true
} else {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot be interpreted 'i18n' option.`)
}
}
} else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
this._i18n.subscribeDataChanging(this)
this._subscribing = true
} else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
this._i18n.subscribeDataChanging(this)
this._subscribing = true
}
},
mounted,
beforeDestroy (): void {
if (!this._i18n) { return }
const self = this
this.$nextTick(() => {
if (self._subscribing) {
self._i18n.unsubscribeDataChanging(self)
delete self._subscribing
}
if (self._i18nWatcher) {
self._i18nWatcher()
self._i18n.destroyVM()
delete self._i18nWatcher
}
if (self._localeWatcher) {
self._localeWatcher()
delete self._localeWatcher
}
})
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"i18n/path" : {
"description": "[required]\nKeypath of the locale message",
"type": "string"
},
"i18n/locale" : {
"description": "[optional]\nLocale to be used in this translation",
"type": "string"
},
"i18n/tag" : {
"description": "[optional]\nWhich tag to render, default is \"span\"",
"type": "string"
},
"i18n/places": {
"description": "[optional after v8.14]\nWill be removed in the next major version, use the slot syntax instead\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#slots-syntax-usage",
"type": "array|object"
},
"i18n-n/value" : {
"description": "[required]\nNumber to be used in formatting",
"type": "number"
},
"i18n-n/format": {
"description": "[optional]\nNumber format name or object with explicit format options",
"type": "string|object"
},
"i18n-n/locale" : {
"description": "[optional]\nLocale to be used in this translation",
"type": "string"
},
"i18n-n/tag" : {
"description": "[optional]\nWhich tag to render, default is `span`",
"type": "string"
}
}
{
"i18n": {
"attributes": [
"path",
"locale",
"tag",
"places"
],
"description": "This is a functional component that can be used when HTML interpolation is needed.\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#basic-usage"
},
"i18n-n": {
"attributes": [
"value",
"format",
"locale",
"tag"
],
"description": "This functional component provides a way to use HTML interpolation in pair with number formatting.\n\nhttp://kazupon.github.io/vue-i18n/guide/number.html#custom-formatting"
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -27,15 +27,17 @@
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<el-tooltip :content="$t('布局大小')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<el-select v-model="locale" placeholder="语言" class="right-menu-item select-nav" @change="localeChange">
<!-- <el-select v-model="locale" placeholder="语言" class="right-menu-item select-nav" @change="localeChange">
<el-option v-for="dict in langDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-select> -->
<dict-selector :type="DICT_TYPE.SYSTEM_LOCALE" v-model="lang" defaultable class="right-menu-item select-nav" />
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
......@@ -44,7 +46,7 @@
</div>
<el-dropdown-menu slot="dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>{{$t('个人中心')}}</el-dropdown-item>
</router-link>
<el-dropdown-item @click.native="setting = true">
<span>布局设置</span>
......@@ -69,23 +71,23 @@ import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import {getLocale, saveLocale} from "@/utils/db";
import {LangEnum} from "@/utils/constants";
import unreadMessage from "@/assets/images/unread-message.png"
import i18n from '@/i18n'
export default {
data() {
return {
unreadMessage,
locale: getLocale(),
// 枚举
langDatas: LangEnum.LANG,
// langDatas: LangEnum.LANG,
notReadTotal:0,//要去取VUEX里面的未读数据总数,我不会,登录之后要调得到当前人未读记录总数接口放到VUEX中
lang: i18n.locale, // 当前语言
}
},
created() {
this.$store.dispatch('getNotMessage');
this.$store.dispatch('getToDoList');
},
},
components: {
Breadcrumb,
TopNav,
......@@ -126,14 +128,22 @@ export default {
}
},
watch:{
lang(val){
console.log('切换了语言', val)
this.$i18n.locale = val
saveLocale(val)
}
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
localeChange(value) {
/* localeChange(value) {
console.log(value)
saveLocale(value)
},
}, */
async logout() {
this.$modal.confirm('确定注销并退出系统吗?', '提示').then(() => {
this.$store.dispatch('LogOut').then(() => {
......
This diff is collapsed.
......@@ -34,6 +34,7 @@ const actions = {
dictDataMap[dictData.dictType].push({
value: dictData.value,
label: dictData.label,
labelEn: dictData.labelEn,
colorType: dictData.colorType,
cssClass: dictData.cssClass,
})
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment