定价管理

This commit is contained in:
邓皓元 2018-12-18 16:46:51 +08:00
parent 65470ba161
commit ea14003efc
50 changed files with 44469 additions and 94 deletions

View File

@ -0,0 +1,85 @@
<?php
namespace App\Domains\Virtual\Http\Controllers;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Domains\Virtual\Repositories\CompanyRepository;
use App\Domains\Virtual\Repositories\PackageRepository;
class FetchController extends Controller
{
protected $request;
/**
* 构造函数,自动注入.
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* 使用名称查找企业
*
* @return void
*/
public function companies(CompanyRepository $companyRepository)
{
return res($this->search($companyRepository), '', 201);
}
/**
* 使用名称查找套餐
*
* @return void
*/
public function packages(PackageRepository $packageRepository)
{
return res($this->search($packageRepository), '', 201);
}
/**
* 统一搜索归并
*
* @param [type] $repository
* @param string $field
* @param integer $limit
* @param string $primaryKey
* @return void
*/
protected function search($repository, $field = 'name', $primaryKey = 'id')
{
$search = $this->request->get('search');
$limit = $this->request->get('limit', 0);
$results = $repository->select([$primaryKey, $field])->get();
if ($search) {
$results = $results->filter(function ($item) use ($search, $field) {
$result = true;
if (strpos($item[$field], $search) === false) {
$result = false;
}
if (strpos(pinyin_abbr($item[$field]), $search) === false) {
$result = false;
}
if (strpos(implode('', pinyin($item[$field])), $search) === false) {
$result = false;
}
return $result;
});
}
$sorted = $results->sortBy($field)->values()->all();
if ($limit) {
return array_slice($sorted, 0, $limit);
}
return $sorted;
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace App\Domains\Virtual\Http\Controllers;
use App\Dicts;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Exceptions\NotExistException;
use App\Domains\Virtual\Services\ProductService;
use App\Domains\Virtual\Repositories\ProductRepository;
class ProductController extends Controller
{
protected $request;
protected $productService;
/**
* 构造函数,自动注入.
*/
public function __construct(Request $request, ProductService $productService)
{
$this->request = $request;
$this->productService = $productService;
}
/**
* 列表.
*
* @return \Illuminate\Http\Response
*/
public function getCompanyProducts(Dicts $dicts)
{
$companyId = $this->request->get('company_id');
$carrierOperator = $this->request->get('carrier_operator');
$products = $this->productService->getCompanyProducts($companyId, $carrierOperator);
$carrierOperators = $dicts->get('carrier_operator');
$products->map(function ($item) use ($carrierOperators) {
$item->carrier_operator = $carrierOperators[$item['package']['carrier_operator']] ?? '未知';
});
return res($products, '定价列表', 201);
}
/**
* 创建.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$attributes = $this->request->all();
$account = $this->productService->store($attributes);
return res($account, '创建成功');
}
/**
* 编辑.
*
* @return \Illuminate\Http\Response
*/
public function update($id)
{
$attributes = $this->request->all();
$attributes['id'] = $id;
$account = $this->productService->store($attributes);
return res($account, '修改成功');
}
/**
* 删除.
*
* @return \Illuminate\Http\Response
*/
public function destroy()
{
$ids = $this->request->ids();
$this->productService->destroy($ids);
return res(true, '删除成功');
}
}

View File

@ -26,6 +26,7 @@ class PackageRepository extends Repository
*/
protected $fieldSearchable = [
'id' => '=',
'name' => 'like',
'created_at' => 'like',
];

View File

@ -6,6 +6,10 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi
// The controllers live in Domains/Virtual/Http/Controllers
$router->get('/', ['as' => 'index', 'uses' => 'VirtualController@index']);
// 名称查找
$router->get('/fetch/companies', ['as' => 'fetch.companies', 'uses' => 'FetchController@companies']);
$router->get('/fetch/packages', ['as' => 'fetch.packages', 'uses' => 'FetchController@packages']);
// 企业管理
$router->get('/companies/index', ['as' => 'companies.index', 'uses' => 'CompanyController@index']);
$router->get('/companies/show/{id}', ['as' => 'companies.show', 'uses' => 'CompanyController@show']);
@ -25,6 +29,12 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi
// $router->post('/company/addresses/update/{id}', ['as' => 'company.addresses.update', 'uses' => 'CompanyAddressController@update']);
// $router->post('/company/addresses/destroy', ['as' => 'company.addresses.destroy', 'uses' => 'CompanyAddressController@destroy']);
// 企业定价管理
$router->get('/products/index', ['as' => 'products.index', 'uses' => 'ProductController@getCompanyProducts']);
$router->post('/products/create', ['as' => 'products.create', 'uses' => 'ProductController@create']);
$router->post('/products/update/{id}', ['as' => 'products.update', 'uses' => 'ProductController@update']);
$router->post('/products/destroy', ['as' => 'products.destroy', 'uses' => 'ProductController@destroy']);
/**
* 需要认证的接口
*/

View File

@ -71,7 +71,7 @@ class CompanyAddressService extends Service
$rule = [
'company_id' => ['required', 'exists:virtual_companies,id'],
'contacts' => ['required', 'display_length:2,32'],
'contacts' => ['required', 'between:2,32'],
'mobile' => ['required', 'cn_phone'],
'area' => ['required', 'max:255'],
'address' => ['required', 'max:255'],
@ -81,7 +81,7 @@ class CompanyAddressService extends Service
'company_id.required' => '请输入企业ID',
'company_id.exists' => '企业不存在或已删除',
'contacts.required' => '联系人不能为空',
'contacts.display_length' => '联系人名称长度不合法',
'contacts.between' => '联系人名称长度不合法',
'mobile.required' => '手机号不能为空',
'mobile.cn_phone' => '手机号不合法',
'area.required' => '请选择区域',

View File

@ -53,8 +53,8 @@ class CompanyService extends Service
$attributes = array_only($attributes, array_merge(app(Company::class)->getFillable()));
$rule = [
'name' => ['required', 'display_length:2,32', Rule::unique($this->companyRepository->getTable(), 'name')->ignore($attributes['id'])],
'contacts' => ['string', 'display_length:2,32'],
'name' => ['required', 'between:2,32', Rule::unique($this->companyRepository->getTable(), 'name')->ignore($attributes['id'])],
'contacts' => ['string', 'between:2,32'],
'mobile' => ['string', 'cn_phone'],
'address' => ['string'],
'extends' => ['array'],
@ -62,9 +62,9 @@ class CompanyService extends Service
$message = [
'name.required' => '请输入企业名称',
'name.display_length' => '企业名称长度不合法',
'name.between' => '企业名称长度不合法',
'name.unique' => '企业名称已经被其他用户所使用',
'contacts.display_length' => '联系人长度不合法',
'contacts.between' => '联系人长度不合法',
'mobile.cn_phone' => '手机号不合法',
];

View File

@ -4,7 +4,9 @@ namespace App\Domains\Virtual\Services;
use App\Core\Service;
use App\Models\Virtual\Product;
use App\Exceptions\NotExistException;
use App\Exceptions\NotAllowedException;
use Illuminate\Support\Facades\Validator;
use App\Domains\Virtual\Repositories\PackageRepository;
use App\Domains\Virtual\Repositories\ProductRepository;
class ProductService extends Service
@ -48,21 +50,48 @@ class ProductService extends Service
*/
public function store(array $attributes = [])
{
$attributes['base_price'] = floatval($attributes['base_price']) * 100;
$attributes['renewal_price'] = floatval($attributes['renewal_price']) * 100;
$rule = [
'name' => ['required', 'between:2,32'],
'company_id' => ['required', 'exists:virtual_companies,id'],
'package_id' => ['required'],
'base_price' => [],
'renewal_price' => [],
'remark' => [],
];
$message = [
'name.required' => '请输入定价名称',
'name.between' => '请输入2-32个字符',
'company_id.required' => '请输入企业ID',
'company_id.exists' => '企业不存在或已删除',
'package_id.required' => '请输入套餐ID',
];
Validator::validate($attributes, $rule, $message);
if (!$package = app(PackageRepository::class)->find($attributes['package_id'])) {
throw new NotExistException('套餐不存在或已删除');
}
$attributes['sn'] = $package['sn'] . '_' . $attributes['company_id'] . '_' . $attributes['base_price'];
if (!$attributes['id']) {
if ($this->productRepository->where('sn', $attributes['sn'])->count()) {
throw new NotAllowedException('已存在相同定价,请勿重复添加');
}
$node = $this->productRepository->create($attributes);
}
if ($attributes['id']) {
if (!$node = $this->productRepository->find($attributes['id'])) {
throw new NotExistException('地址不存在或已删除');
throw new NotExistException('定价不存在或已删除');
}
if ($this->productRepository->where('sn', $attributes['sn'])->where('id', '<>', $attributes['id'])->count()) {
throw new NotAllowedException('已存在相同定价,请核对后重试');
}
$this->productRepository->setModel($node)->update($attributes);

View File

@ -52,6 +52,9 @@ class AppServiceProvider extends ServiceProvider
// Image
$this->app->register(\Intervention\Image\ImageServiceProviderLumen::class);
// 拼音
$this->app->register(\Overtrue\LaravelPinyin\ServiceProvider::class);
// SMS
$this->app->singleton('sms', function ($app) {
return $app->loadComponent('sms', \Dipper\Sms\SmsServiceProvider::class);

View File

@ -16,7 +16,8 @@
"dipper/flashmessage": ">=1.0.0",
"dipper/jwt-auth": ">=1.0.0",
"dipper/sms": ">=1.0.0",
"jeremeamia/SuperClosure": "^2.4"
"jeremeamia/SuperClosure": "^2.4",
"overtrue/laravel-pinyin": "~3.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",

102
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ea5d9710363a1e1208b542ba36020b4b",
"content-hash": "0792baf15c321ccef7691753e5b70a7c",
"packages": [
{
"name": "asm89/stack-cors",
@ -3980,6 +3980,106 @@
],
"time": "2018-10-10T09:24:14+00:00"
},
{
"name": "overtrue/laravel-pinyin",
"version": "3.0.5",
"source": {
"type": "git",
"url": "https://github.com/overtrue/laravel-pinyin.git",
"reference": "4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/laravel-pinyin/zipball/4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc",
"reference": "4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc",
"shasum": ""
},
"require": {
"overtrue/pinyin": "~3.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Overtrue\\LaravelPinyin\\ServiceProvider"
],
"aliases": {
"Pinyin": "Overtrue\\LaravelPinyin\\Facades\\Pinyin"
}
}
},
"autoload": {
"psr-4": {
"Overtrue\\LaravelPinyin\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "overtrue",
"email": "anzhengchao@gmail.com"
}
],
"description": "Chinese to Pinyin translator.",
"keywords": [
"Chinese",
"Pinyin",
"laravel",
"overtrue"
],
"time": "2017-07-02T22:06:52+00:00"
},
{
"name": "overtrue/pinyin",
"version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/overtrue/pinyin.git",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"autoload": {
"psr-4": {
"Overtrue\\Pinyin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Carlos",
"homepage": "http://github.com/overtrue"
}
],
"description": "Chinese to pinyin translator.",
"homepage": "https://github.com/overtrue/pinyin",
"keywords": [
"Chinese",
"Pinyin",
"cn2pinyin"
],
"time": "2017-07-10T07:20:01+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.99",

View File

@ -197,6 +197,7 @@ class CreateBaseTables extends Migration
$table->integer('package_id')->unsigned()->default(0)->comment('套餐ID');
$table->integer('base_price')->unsigned()->default(0)->comment('基础价格');
$table->integer('renewal_price')->unsigned()->default(0)->comment('续费价格');
$table->text('remark')->nullable()->comment('备注');
$table->timestamps();
$table->softDeletes();

View File

@ -17,6 +17,7 @@
"jquery": "^3.3.1",
"js-cookie": "^2.2.0",
"moment": "^2.22.2",
"pinyin-engine": "^1.1.0",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",

View File

@ -0,0 +1,31 @@
/**
* 名称操作
*/
/**
* [companies 企业列表]
* @param {[type]} name [description]
* @return {[type]} [description]
*/
export function companies(name, limit = 5) {
return service.get('api/virtual/fetch/companies', {
params: {
search: name,
limit
}
});
}
/**
* [packages 企业列表]
* @param {[type]} name [description]
* @return {[type]} [description]
*/
export function packages(name, limit = 5) {
return service.get('api/virtual/fetch/packages', {
params: {
search: name,
limit
}
});
}

View File

@ -0,0 +1,42 @@
/**
* 定价管理
*/
/**
* [index 定价列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function index(data) {
return service.get('api/virtual/products/index', {
params: data
});
}
/**
* [create 创建定价]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function create(data) {
return serviceForm.post('api/virtual/products/create', data);
}
/**
* [update 修改定价]
* @param {[type]} data [description]
* @param {[type]} id [角色id]
* @return {[type]} [description]
*/
export function update(data, id) {
return serviceForm.post(`api/virtual/products/update/${id}`, data);
}
/**
* [destroy 删除定价]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function destroy(data) {
return service.post('api/virtual/products/destroy', data);
}

View File

@ -7,11 +7,12 @@ import Vue from 'vue';
import iView from 'iview';
import Cookies from 'js-cookie';
import Treeselect from '@riophae/vue-treeselect';
import { service, serviceForm, axios } from 'service/service';
import { service, serviceForm } from 'service/service';
import App from './App';
import router from './router';
import store from './store';
import mixins from './mixins';
import complete from './mixins/complete';
import md5 from 'blueimp-md5';
import jquery from 'jquery';
@ -29,6 +30,7 @@ Vue.config.productionTip = false;
Vue.use(iView);
Vue.mixin(mixins);
Vue.mixin(complete);
Vue.use(base);
Vue.component('Treeselect', Treeselect);

View File

@ -0,0 +1,65 @@
import * as FETCH from 'api/virtual/fetch';
import PinyinEngine from 'pinyin-engine';
export default {
data() {
return {
completeCompanyInitialized: false,
completeCompaniesPinyinEngine: null,
completeCompanies: [],
completeHandledCompanies: [],
completePackageInitialized: false,
completePackagesPinyinEngine: null,
completePackages: [],
completeHandledPackages: []
};
},
methods: {
initCompleteCompanies() {
return new Promise((resolve, reject) => {
this.completeCompanyInitialized = true;
FETCH.companies(null, 0).then(res => {
if (res.code === 0) {
this.completeCompanies = res.data;
this.completeCompaniesPinyinEngine = new PinyinEngine(res.data, ['name']);
resolve(res.data);
}
reject(res);
});
});
},
handleCompleteCompanies(value) {
if (!this.completeCompanyInitialized) {
this.initCompleteCompanies();
}
if (this.completeCompaniesPinyinEngine) {
this.completeHandledCompanies = this.completeCompaniesPinyinEngine.query(value);
}
},
initCompletePackages() {
return new Promise((resolve, reject) => {
this.completePackageInitialized = true;
FETCH.packages(null, 0).then(res => {
if (res.code === 0) {
this.completePackages = res.data;
this.completePackagesPinyinEngine = new PinyinEngine(res.data, ['name']);
resolve(res.data);
}
reject(res);
});
});
},
handleCompletePackages(value) {
if (!this.completePackageInitialized) {
this.initCompletePackages();
}
if (this.completePackagesPinyinEngine) {
this.completeHandledPackages = this.completePackagesPinyinEngine.query(value);
}
}
}
};

View File

@ -18,7 +18,8 @@ const routes = [
{ path: '/accounts', name: 'Accounts', component: load('user/accounts/index'), meta: { title: '账号管理' } },
{ path: '/iframe', name: 'Iframe', component: load('iframe/index'), meta: { title: 'iframe' } },
{ path: '/companies', name: 'Companies', component: load('virtual/companies/index'), meta: { title: '企业管理' } },
{ path: '/company/accounts', name: 'CompanyAccounts', component: load('virtual/company_accounts/index'), meta: { title: '账号管理' } }
{ path: '/company/accounts', name: 'CompanyAccounts', component: load('virtual/company_accounts/index'), meta: { title: '账号管理' } },
{ path: '/products', name: 'Products', component: load('virtual/products/index'), meta: { title: '定价管理' } }
]
},
{ path: '*', redirect: { path: '/home' } }

View File

@ -1,23 +1,24 @@
<template>
<div class="layout-nav">
<div class="logo-wrap">
<img class="small" :src="CONFIG.logo_small" v-if="collapsed">
<img class="big" :src="CONFIG.logo_big" v-else>
<img :src="CONFIG.logo_small" class="small" v-if="collapsed">
<img :src="CONFIG.logo_big" class="big" v-else>
</div>
<div class="nav-wrap" v-if="left_menu.list.length">
<Menu
ref="sideMenu"
v-show="!collapsed"
width="auto"
theme="dark"
accordion
:active-name="left_menu.active_name"
:open-names="left_menu.open_names"
@on-select="menuChange">
@on-select="menuChange"
accordion
ref="sideMenu"
theme="dark"
v-show="!collapsed"
width="auto"
>
<template v-for="(item,index) in left_menu.list">
<side-menu-item v-if="item.menus && item.menus.length" :menu="item"></side-menu-item>
<side-menu-item :menu="item" v-if="item.menus && item.menus.length"></side-menu-item>
<menuItem v-else :name="item.id">
<menuItem :name="item.id" v-else>
<Icon :type="item.icon" v-if="item.icon"/>
<span>{{item.title}}</span>
</menuItem>
@ -26,7 +27,7 @@
<div class="menu-collapsed" v-show="collapsed">
<template v-for="(item,index) in left_menu.list">
<collapsed-menu :menu="item" :level="1"></collapsed-menu>
<collapsed-menu :level="1" :menu="item"></collapsed-menu>
</template>
</div>
</div>
@ -35,8 +36,8 @@
<script>
//
import sideMenuItem from 'views/layout/menu/side_menu_item';
import collapsedMenu from 'views/layout/menu/collapsed_menu';
import sideMenuItem from "views/layout/menu/side_menu_item";
import collapsedMenu from "views/layout/menu/collapsed_menu";
export default {
components: {
@ -50,7 +51,7 @@
}
},
watch: {
['left_menu.open_names'](){
["left_menu.open_names"]() {
if (this.$refs.sideMenu && this.left_menu.list.length) {
this.$nextTick(() => {
this.$refs.sideMenu.updateOpened();
@ -69,7 +70,7 @@
const menu = this.permissions_object[mid];
switch (menu.open) {
case 0: //iframe
this.$router.push({path:'/iframe',query:{mid:menu.id}});
this.$router.push({ path: "/iframe", query: { mid: menu.id } });
break;
case 1: //
window.open(menu.path);
@ -77,7 +78,11 @@
case 2: //
const top = (window.outerHeight - menu.height) / 2;
const left = (window.outerWidth - menu.width) / 2;
window.open(menu.path,'',`width=${menu.width},height=${menu.height},top=${top},left=${left}`);
window.open(
menu.path,
"",
`width=${menu.width},height=${menu.height},top=${top},left=${left}`
);
break;
case 3: //vue
this.$router.push({ path: menu.path, query: { mid: menu.id } });
@ -85,6 +90,6 @@
}
}
}
}
};
</script>

View File

@ -27,7 +27,9 @@
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<Input clearable placeholder="请输入企业名称" v-model.trim="params.name"></Input>
<AutoComplete @on-search="handleCompleteCompanies" clearable icon="ios-search" placeholder="请输入企业名称" v-model.trim="params.name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledCompanies">{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">

View File

@ -1,5 +1,4 @@
import * as API from 'api/virtual/companies';
export default {
name: 'Companies',
components: {

View File

@ -23,7 +23,9 @@
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<Input clearable placeholder="请输入企业名称" v-model.trim="params.name"></Input>
<AutoComplete @on-search="handleCompleteCompanies" clearable icon="ios-search" placeholder="请输入企业名称" v-model.trim="params.name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledCompanies">{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
@ -59,7 +61,13 @@
></Page>
</div>
<ui-edit :data="editObj.data" :show.sync="editObj.show" :isUpdate.sync="editObj.isUpdate" @add-success="index" @update-success="index(list_data.current_page)"></ui-edit>
<ui-edit
:data="editObj.data"
:isUpdate.sync="editObj.isUpdate"
:show.sync="editObj.show"
@add-success="index"
@update-success="index(list_data.current_page)"
></ui-edit>
</div>
</template>

View File

@ -0,0 +1,58 @@
<template>
<Modal :closable="false" :mask-closable="false" :title="isUpdate ? '编辑定价' : '添加定价'" @on-visible-change="visibleChange" v-model="my_show">
<div class="page-edit-wrap uinn-lr20">
<ui-loading :show="page_loading.show"></ui-loading>
<ul>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require">*</span>定价名称:
</div>
<div class="ui-list-content">
<Input :maxlength="32" v-model.trim="params.name"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require">*</span>选择套餐:
</div>
<div class="ui-list-content">
<Select filterable v-model.trim="params.package_id">
<Option :key="item.id" :value="item.id" v-for="item in completePackages">{{ item.name }}</Option>
</Select>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">基础价格</div>
<div class="ui-list-content">
<InputNumber :max="10000" :min="0" :step="0.1" v-model.trim="params.base_price"></InputNumber>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">续费价格</div>
<div class="ui-list-content">
<InputNumber :max="10000" :min="0" :step="0.1" v-model.trim="params.renewal_price"></InputNumber>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">备注:</div>
<div class="ui-list-content">
<p>
<Input :maxlength="32" v-model.trim="params.remark"></Input>
</p>
</div>
</li>
</ul>
</div>
<footer class="ta-c" slot="footer">
<Button @click="clear" class="w-80" ghost type="primary">取消</Button>
<Button :loading="loading" @click="ok" class="w-80" type="primary">提交</Button>
</footer>
</Modal>
</template>
<script src="./js/edit.js"></script>

View File

@ -0,0 +1,101 @@
<template>
<div class="page-wrap">
<ui-loading :show="page_loading.show"></ui-loading>
<div class="product-content">
<div class="nav">
<div class="search umar-t5">
<AutoComplete @on-search="handleSearchCompanies" placeholder="输入名称进行过滤"></AutoComplete>
</div>
<div class="box">
<CellGroup @on-click="index" v-for="item in companies">
<Cell :name="item.id" :selected="item.id == params.company_id ? true : false" :title="item.name"/>
</CellGroup>
</div>
</div>
<div class="info-wrap">
<div class="page-handle-wrap">
<ul class="handle-wraper bd-b">
<li class="f-l">
<div class="text-exp">
<b>全部信息</b>
</div>
</li>
<li class="f-r" v-if="params.company_id">
<div class="handle-item">
<Button @click="openEdit(true, null)" icon="md-add" type="primary" v-has="'create'">添加定价</Button>
</div>
<div class="handle-item">
<Button @click="search.show=!search.show" ghost icon="ios-search" type="primary">搜索</Button>
</div>
<div class="handle-item">
<Button @click="index(1)" icon="md-refresh">刷新</Button>
</div>
</li>
</ul>
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable v-model="params.carrier_operator">
<Option :value="'0'">联通</Option>
<Option :value="'1'">移动</Option>
<Option :value="'2'">电信</Option>
</Select>
</li>
</ul>
<ul class="handle-wraper">
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" ghost type="primary">立即搜索</Button>
</div>
<div class="handle-item">
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
</div>
</li>
</ul>
</div>
</div>
<div class="page-list-wrap">
<Table :columns="columns" :data="data ? data : []"></Table>
</div>
</div>
</div>
<ui-edit :data="editObj.data" :isUpdate="editObj.isUpdate" :show.sync="editObj.show" @add-success="index" @update-success="index"></ui-edit>
</div>
</template>
<script src="./js/index.js"></script>
<style lang="less" scoped>
.page-wrap {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 0;
.product-content {
display: flex;
height: 100%;
.nav {
width: 20%;
background: #fff;
padding: 10px;
.box {
margin-top: 10px;
overflow-x: hidden;
overflow-y: auto;
height: calc(100% - 5px);
}
}
.info-wrap {
width: 80%;
}
}
}
</style>

View File

@ -0,0 +1,112 @@
import * as API from 'api/virtual/products';
export default {
props: {
show: {
type: Boolean,
default: false
},
isUpdate: {
type: Boolean,
default: false
},
data: {
type: Object,
default() {
return null;
}
}
},
data() {
return {
my_show: false,
loading: false,
params: {
name: '',
company_id: '',
package_id: '',
base_price: 0,
renewal_price: 0,
remark: ''
}
};
},
watch: {
show(bool) {
this.my_show = bool;
if (bool) {
if (this.data) {
for (let k in this.data) {
if (k in this.params) {
this.params[k] = this.data[k];
}
}
}
}
if (!this.completePackageInitialized) {
this.initCompletePackages();
}
}
},
methods: {
ok() {
if (!this.params.company_id) {
this.$Message.info('非法请求');
}
if (!this.params.name) {
this.$Message.info('请输入定价名称');
return;
}
if (!this.params.package_id) {
this.$Message.info('请选择一个套餐');
return;
}
if (this.isUpdate) {
// 编辑
API.update(this.params, this.data.id).then(res => {
this.loading = false;
if (res.code == 0) {
this.$emit('update-success');
this.$Message.success('更新成功');
this.clear();
}
}).catch(err => {
this.loading = false;
});
} else {
// 添加
API.create(this.params).then(res => {
this.loading = false;
if (res.code == 0) {
this.$emit('add-success');
this.$Message.success('添加成功');
this.clear();
}
}).catch(err => {
this.loading = false;
});
}
},
visibleChange(bool) {
if (!bool) {
this.$emit('update:show', false);
}
},
clear() {
for (let k in this.params) {
if (k === 'base_price' || k === 'renewal_price') {
this.params[k] = 0;
} else {
this.params[k] = '';
}
}
this.my_show = false;
}
}
};

View File

@ -0,0 +1,216 @@
import * as API from 'api/virtual/products';
export default {
name: 'Products',
components: {
UiEdit: resolve => require(['views/virtual/products/edit'], resolve)
},
data() {
return {
params: {
company_id: null,
carrier_operator: null
},
trashed: '',
editObj: {
show: false,
isUpdate: false,
data: null
},
search: {
show: false
},
companies: [],
data: [],
columns: [
{
title: 'ID',
key: 'id',
width: 80
},
{
title: '定价名称',
key: 'name'
},
{
title: '套餐名称',
key: '',
render: (h, { row, column, index }) => {
if (row.package) {
return h('span', row.package.name);
}
}
},
{
title: '定价名称',
key: 'name'
},
{
title: '套餐价格',
key: '',
render: (h, { row, column, index }) => {
let price = Number(row.base_price / 100);
return h('span', price.toFixed(2));
}
},
{
title: '续费价格',
key: '',
render: (h, { row, column, index }) => {
let price = Number(row.renewal_price / 100);
return h('span', price.toFixed(2));
}
},
{
title: '运营商',
key: 'carrier_operator'
},
{
title: '更新时间',
key: 'updated_at',
width: 170
},
{
title: '操作',
key: 'action',
width: 170,
render: (h, {
row,
column,
index
}) => {
let html = [];
if (this.haveJurisdiction('update')) {
html.push(h('Button', {
props: {
type: 'primary',
size: 'small',
disabled: false,
icon: 'ios-create'
},
class: ['btn'],
on: {
click: (event) => {
this.openEdit(true, row);
}
}
}, '编辑'));
}
if (this.haveJurisdiction('destroy')) {
html.push(h('Button', {
props: {
type: 'error',
size: 'small',
disabled: false,
icon: 'md-trash'
},
class: ['btn'],
on: {
click: () => {
this.$Modal.confirm({
title: '提示',
content: '删除后该定价不可使用,请谨慎操作',
onOk: () => {
API.destroy({
ids: row.id
}).then(res => {
if (res.code == 0) {
this.$Message.success('删除成功');
this.request();
}
});
}
});
}
}
}, '删除'));
}
if (html.length) {
return h('div', html);
}
}
}
]
};
},
created() {
// this.index(1);
this.initCompleteCompanies().then(res => {
this.companies = res;
}).catch(err => {
this.$Message.error(err.message);
});
},
methods: {
/**
* [index 列表]
* @param {Number} company_id [description]
* @return {[type]} [description]
*/
index(company_id = null) {
if (company_id) {
this.params.company_id = company_id;
}
this.isShowLoading(true);
API.index(this.params).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
},
/**
* [openEdit 打开编辑弹窗]
* @return {[type]} [description]
*/
openEdit(show, row = null) {
let isUpdate = false;
let data = {};
if (row) {
isUpdate = true;
data = JSON.parse(JSON.stringify(row));
data.base_price = data.base_price ? Number(data.base_price / 100) : 0;
data.renewal_price = data.renewal_price ? Number(data.renewal_price / 100) : 0;
data.company_id = this.params.company_id;
} else {
data = { company_id: this.params.company_id };
}
this.editObj = { show, data, isUpdate };
},
/**
* [request 刷新]
* @return {[type]} [description]
*/
request() {
this.index();
},
resetSearch() {
for (let k in this.params) {
if (k !== 'company_id') {
this.params[k] = null;
}
}
this.index();
},
handleSearchCompanies(value) {
if (value === '') {
this.companies = this.completeCompanies;
return;
}
if (this.completeCompaniesPinyinEngine) {
this.companies = this.completeCompaniesPinyinEngine.query(value);
}
}
}
};

View File

@ -67,5 +67,6 @@ return array(
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'7b310ffe822e5ee3a4f219c3bf86fd38' => $vendorDir . '/dipper/foundation/src/helpers.php',
'bee9632da3ca00a99623b9c35d0c4f8b' => $vendorDir . '/laravel/lumen-framework/src/helpers.php',
'cda2d2f579338909929d3104d0afc501' => $vendorDir . '/overtrue/laravel-pinyin/src/helpers.php',
'5c496105b995e0fd9efd783e0ae26dcf' => $baseDir . '/app/helpers.php',
);

View File

@ -39,6 +39,8 @@ return array(
'Predis\\' => array($vendorDir . '/predis/predis/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
'Overtrue\\Pinyin\\' => array($vendorDir . '/overtrue/pinyin/src'),
'Overtrue\\LaravelPinyin\\' => array($vendorDir . '/overtrue/laravel-pinyin/src'),
'Namshi\\JOSE\\' => array($vendorDir . '/namshi/jose/src/Namshi/JOSE'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'MongoDB\\' => array($vendorDir . '/mongodb/mongodb/src'),

View File

@ -68,6 +68,7 @@ class ComposerStaticInite79258a3e34ad3e251999111d9f334d9
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'7b310ffe822e5ee3a4f219c3bf86fd38' => __DIR__ . '/..' . '/dipper/foundation/src/helpers.php',
'bee9632da3ca00a99623b9c35d0c4f8b' => __DIR__ . '/..' . '/laravel/lumen-framework/src/helpers.php',
'cda2d2f579338909929d3104d0afc501' => __DIR__ . '/..' . '/overtrue/laravel-pinyin/src/helpers.php',
'5c496105b995e0fd9efd783e0ae26dcf' => __DIR__ . '/../..' . '/app/helpers.php',
);
@ -123,6 +124,11 @@ class ComposerStaticInite79258a3e34ad3e251999111d9f334d9
'PhpParser\\' => 10,
'PhpOffice\\PhpSpreadsheet\\' => 25,
),
'O' =>
array (
'Overtrue\\Pinyin\\' => 16,
'Overtrue\\LaravelPinyin\\' => 23,
),
'N' =>
array (
'Namshi\\JOSE\\' => 12,
@ -363,6 +369,14 @@ class ComposerStaticInite79258a3e34ad3e251999111d9f334d9
array (
0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
),
'Overtrue\\Pinyin\\' =>
array (
0 => __DIR__ . '/..' . '/overtrue/pinyin/src',
),
'Overtrue\\LaravelPinyin\\' =>
array (
0 => __DIR__ . '/..' . '/overtrue/laravel-pinyin/src',
),
'Namshi\\JOSE\\' =>
array (
0 => __DIR__ . '/..' . '/namshi/jose/src/Namshi/JOSE',

View File

@ -4389,6 +4389,110 @@
"php"
]
},
{
"name": "overtrue/laravel-pinyin",
"version": "3.0.5",
"version_normalized": "3.0.5.0",
"source": {
"type": "git",
"url": "https://github.com/overtrue/laravel-pinyin.git",
"reference": "4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/laravel-pinyin/zipball/4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc",
"reference": "4ca98a67cc2cd53ce98ee43dddbc5f5093cdbacc",
"shasum": ""
},
"require": {
"overtrue/pinyin": "~3.0"
},
"time": "2017-07-02T22:06:52+00:00",
"type": "library",
"extra": {
"laravel": {
"providers": [
"Overtrue\\LaravelPinyin\\ServiceProvider"
],
"aliases": {
"Pinyin": "Overtrue\\LaravelPinyin\\Facades\\Pinyin"
}
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Overtrue\\LaravelPinyin\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "overtrue",
"email": "anzhengchao@gmail.com"
}
],
"description": "Chinese to Pinyin translator.",
"keywords": [
"Chinese",
"Pinyin",
"laravel",
"overtrue"
]
},
{
"name": "overtrue/pinyin",
"version": "3.0.6",
"version_normalized": "3.0.6.0",
"source": {
"type": "git",
"url": "https://github.com/overtrue/pinyin.git",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"time": "2017-07-10T07:20:01+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Overtrue\\Pinyin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Carlos",
"homepage": "http://github.com/overtrue"
}
],
"description": "Chinese to pinyin translator.",
"homepage": "https://github.com/overtrue/pinyin",
"keywords": [
"Chinese",
"Pinyin",
"cn2pinyin"
]
},
{
"name": "paragonie/random_compat",
"version": "v9.99.99",

22
vendor/overtrue/laravel-pinyin/LICENSE vendored Executable file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 安正超
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.

92
vendor/overtrue/laravel-pinyin/README.md vendored Executable file
View File

@ -0,0 +1,92 @@
# Laravel-pinyin
Chinese to Pinyin translator for Laravel5 / Lumen based on [overtrue/pinyin](https://github.com/overtrue/pinyin).
[![Latest Stable Version](https://poser.pugx.org/overtrue/laravel-pinyin/v/stable.svg)](https://packagist.org/packages/overtrue/laravel-pinyin) [![Total Downloads](https://poser.pugx.org/overtrue/laravel-pinyin/downloads.svg)](https://packagist.org/packages/overtrue/laravel-pinyin) [![Latest Unstable Version](https://poser.pugx.org/overtrue/laravel-pinyin/v/unstable.svg)](https://packagist.org/packages/overtrue/laravel-pinyin) [![License](https://poser.pugx.org/overtrue/laravel-pinyin/license.svg)](https://packagist.org/packages/overtrue/laravel-pinyin)
## Install
```shell
composer require "overtrue/laravel-pinyin:~3.0"
```
## For Laravel
Add the following line to the section `providers` of `config/app.php`:
```php
'providers' => [
//...
Overtrue\LaravelPinyin\ServiceProvider::class,
],
```
as optional, you can use facade:
```php
'aliases' => [
//...
'Pinyin' => Overtrue\LaravelPinyin\Facades\Pinyin::class,
],
```
## For Lumen
Add the following line to `bootstrap/app.php` after `// $app->withEloquent();`
```php
...
// $app->withEloquent();
$app->register(Overtrue\LaravelPinyin\ServiceProvider::class);
...
```
## Usage
you can get the instance of `Overtrue\Pinyin\Pinyin` from app container:
```php
$pinyin = app('pinyin');
echo $pinyin->sentence('带着希望去旅行,比到达终点更美好');
// dài zhe xī wàng qù lǔ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo
```
There are more convenient functions:
| function | method |
| ------------- | --------------------------------------------------- |
| `pinyin()` | `app('pinyin')->convert()` |
| `pinyin_abbr()` | `app('pinyin')->abbr()` |
| `pinyin_permalink` | `app('pinyin')->permalink()` |
| `pinyin_sentence` | `app('pinyin')->sentence()` |
```php
var_dump(pinyin('带着希望去旅行,比到达终点更美好'));
// ["dai", "zhe", "xi", "wang", "qu", "lv", "xing", "bi", "dao", "da", "zhong", "dian", "geng", "mei", "hao"]
var_dump(pinyin_abbr('带着希望去旅行'));
// dzxwqlx
...
```
Using facade:
```php
use Pinyin; // Facade class, NOT Overtrue\Pinyin\Pinyin
var_dump(Pinyin::convert('带着希望去旅行'));
// ["dai", "zhe", "xi", "wang", "qu", "lv", "xing"]
echo Pinyin::sentence('带着希望去旅行,比到达终点更美好');
// dài zhe xī wàng qù lǔ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo
```
About `overtrue/pinyin` specific configuration and use, refer to: [overtrue/pinyin](https://github.com/overtrue/pinyin)
## License
MIT

38
vendor/overtrue/laravel-pinyin/composer.json vendored Executable file
View File

@ -0,0 +1,38 @@
{
"name": "overtrue/laravel-pinyin",
"description": "Chinese to Pinyin translator.",
"keywords": [
"laravel",
"pinyin",
"chinese",
"overtrue"
],
"require": {
"overtrue/pinyin": "~3.0"
},
"autoload": {
"psr-4": {
"Overtrue\\LaravelPinyin\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"extra": {
"laravel": {
"providers": [
"Overtrue\\LaravelPinyin\\ServiceProvider"
],
"aliases": {
"Pinyin": "Overtrue\\LaravelPinyin\\Facades\\Pinyin"
}
}
},
"license": "MIT",
"authors": [
{
"name": "overtrue",
"email": "anzhengchao@gmail.com"
}
]
}

View File

@ -0,0 +1,14 @@
<?php
namespace Overtrue\LaravelPinyin\Facades;
use Illuminate\Support\Facades\Facade;
use Overtrue\Pinyin\Pinyin as Accessor;
class Pinyin extends Facade
{
public static function getFacadeAccessor()
{
return Accessor::class;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Overtrue\LaravelPinyin;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
use Overtrue\Pinyin\Pinyin;
class ServiceProvider extends LaravelServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the provider.
*
* @return void
*/
public function register()
{
$this->app->singleton(Pinyin::class, function($app)
{
return new Pinyin();
});
$this->app->alias(Pinyin::class, 'pinyin');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [Pinyin::class, 'pinyin'];
}
}

View File

@ -0,0 +1,91 @@
<?php
use Illuminate\Support\Facades\Log;
use Overtrue\Pinyin\Pinyin;
if (! function_exists('pinyin')) {
/**
* Get the Pinyin of given string.
*
* @param string $string
* @param string $option
*
* @return string
*/
function pinyin($string, $option = Pinyin::NONE)
{
return app(Pinyin::class)->convert($string, $option);
}
} else {
Log::warning('There exist multiple function "pinyin".');
}
if (! function_exists('pinyin_abbr')) {
/**
* Get the fist letters of given string.
*
* @param string $string
* @param string $delimiter
*
* @return string
*/
function pinyin_abbr($string, $delimiter = '')
{
return app(Pinyin::class)->abbr($string, $delimiter);
}
} else {
Log::warning('There exist multiple function "pinyin_abbr".');
}
if (! function_exists('pinyin_permlink')) {
/**
* Get a pinyin permalink from string.
*
* @param string $string
* @param string $delimiter
*
* @return string
*
* @deprecated since version 3.0.1. Use the "pinyin_permalink" method instead.
*/
function pinyin_permlink($string, $delimiter = '-')
{
return app(Pinyin::class)->permalink($string, $delimiter);
}
} else {
Log::warning('There exist multiple function "pinyin_permlink".');
}
if (! function_exists('pinyin_permalink')) {
/**
* Get a pinyin permalink from string.
*
* @param string $string
* @param string $delimiter
*
* @return string
*/
function pinyin_permalink($string, $delimiter = '-')
{
return app(Pinyin::class)->permalink($string, $delimiter);
}
} else {
Log::warning('There exist multiple function "pinyin_permalink".');
}
if (! function_exists('pinyin_sentence')) {
/**
* Get the fist pinyin and letters of given string.
*
* @param string $string
* @param string $tone
*
* @return string
*/
function pinyin_sentence($string, $tone = false)
{
return app(Pinyin::class)->sentence($string, $tone);
}
} else {
Log::warning('There exist multiple function "pinyin_sentence".');
}

21
vendor/overtrue/pinyin/LICENSE vendored Executable file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 安正超
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.

120
vendor/overtrue/pinyin/README.md vendored Executable file
View File

@ -0,0 +1,120 @@
Pinyin
======
[![Build Status](https://travis-ci.org/overtrue/pinyin.svg?branch=master)](https://travis-ci.org/overtrue/pinyin)
[![Latest Stable Version](https://poser.pugx.org/overtrue/pinyin/v/stable.svg)](https://packagist.org/packages/overtrue/pinyin) [![Total Downloads](https://poser.pugx.org/overtrue/pinyin/downloads.svg)](https://packagist.org/packages/overtrue/pinyin) [![Latest Unstable Version](https://poser.pugx.org/overtrue/pinyin/v/unstable.svg)](https://packagist.org/packages/overtrue/pinyin) [![License](https://poser.pugx.org/overtrue/pinyin/license.svg)](https://packagist.org/packages/overtrue/pinyin)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/overtrue/pinyin/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/overtrue/pinyin/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
<p align="center">
<br>
 <b>创造不息,交付不止</b>
<br>
<a href="https://www.yousails.com">
<img src="https://yousails.com/banners/brand.png" width=350>
</a>
</p>
:cn: 基于 [CC-CEDICT](http://cc-cedict.org/wiki/) 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案。
## 安装
使用 Composer 安装:
```
composer require "overtrue/pinyin:~3.0"
```
## 使用
可选转换方案:
- 内存型,适用于服务器内存空间较富余,优点:转换快
- 小内存型(默认),适用于内存比较紧张的环境,优点:占用内存小,转换不如内存型快
- I/O型适用于虚拟机内存限制比较严格环境。优点非常微小内存消耗。缺点转换慢不如内存型转换快,php >= 5.5
### 拼音数组
```php
use Overtrue\Pinyin\Pinyin;
// 小内存型
$pinyin = new Pinyin(); // 默认
// 内存型
// $pinyin = new Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
// I/O型
// $pinyin = new Pinyin('Overtrue\Pinyin\GeneratorFileDictLoader');
$pinyin->convert('带着希望去旅行,比到达终点更美好');
// ["dai", "zhe", "xi", "wang", "qu", "lv", "xing", "bi", "dao", "da", "zhong", "dian", "geng", "mei", "hao"]
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_UNICODE);
// ["dài","zhe","xī","wàng","qù","lǚ","xíng","bǐ","dào","dá","zhōng","diǎn","gèng","měi","hǎo"]
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_ASCII);
//["dai4","zhe","xi1","wang4","qu4","lv3","xing2","bi3","dao4","da2","zhong1","dian3","geng4","mei3","hao3"]
```
- 小内存型: 将字典分片载入内存
- 内存型: 将所有字典预先载入内存
- I/O型: 不载入内存将字典使用文件流打开逐行遍历并运用php5.5生成器(yield)特性分配单行内存
选项:
| 选项 | 描述 |
| ------------- | ---------------------------------------------------|
| `PINYIN_NONE` | 不带音调输出: `mei hao` |
| `PINYIN_ASCII` | 带数字式音调: `mei3 hao3` |
| `PINYIN_UNICODE` | UNICODE 式音调:`měi hǎo` |
### 生成用于链接的拼音字符串
```php
$pinyin->permalink('带着希望去旅行'); // dai-zhe-xi-wang-qu-lv-xing
$pinyin->permalink('带着希望去旅行', '.'); // dai.zhe.xi.wang.qu.lv.xing
```
### 获取首字符字符串
```php
$pinyin->abbr('带着希望去旅行'); // dzxwqlx
$pinyin->abbr('带着希望去旅行', '-'); // d-z-x-w-q-l-x
```
### 翻译整段文字为拼音
将会保留中文字符:`,。 “ ” ` 并替换为对应的英文符号。
```php
$pinyin->sentence('带着希望去旅行,比到达终点更美好!');
// dai zhe xi wang qu lv xing, bi dao da zhong dian geng mei hao!
$pinyin->sentence('带着希望去旅行,比到达终点更美好!', true);
// dài zhe xī wàng qù lǚ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo!
```
### 翻译姓名
姓名的姓的读音有些与普通字不一样,比如 ‘单’ 常见的音为 `dan`,而作为姓的时候读 `shan`
```php
$pinyin->name('单某某'); // ['shan', 'mou', 'mou']
$pinyin->name('单某某', PINYIN_UNICODE); // ["shàn","mǒu","mǒu"]
```
## 在 Laravel 中使用
独立的包在这里:[overtrue/laravel-pinyin](https://github.com/overtrue/laravel-pinyin)
## Contribution
欢迎提意见及完善补充词库 [`overtrue/pinyin-dictionary-maker`](https://github.com/overtrue/pinyin-dictionary-maker/tree/master/patches) :kiss:
## 参考
- [详细参考资料](https://github.com/overtrue/pinyin-resources)
# License
MIT

33
vendor/overtrue/pinyin/composer.json vendored Executable file
View File

@ -0,0 +1,33 @@
{
"name": "overtrue/pinyin",
"description": "Chinese to pinyin translator.",
"keywords": [
"chinese",
"pinyin",
"cn2pinyin"
],
"homepage": "https://github.com/overtrue/pinyin",
"license": "MIT",
"authors": [
{
"name": "Carlos",
"homepage": "http://github.com/overtrue"
}
],
"autoload": {
"psr-4": {
"Overtrue\\Pinyin\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Overtrue\\Pinyin\\Test\\": "tests/"
}
},
"require": {
"php":">=5.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
}
}

84
vendor/overtrue/pinyin/data/surnames vendored Normal file
View File

@ -0,0 +1,84 @@
<?php
return array (
'万俟' => ' mò qí',
'尉迟' => ' yù chí',
'单于' => ' chán yú',
'不' => ' fǒu',
'沈' => ' shěn',
'称' => ' chēng',
'车' => ' chē',
'万' => ' wàn',
'汤' => ' tāng',
'阿' => ' ā',
'丁' => ' dīng',
'强' => ' qiáng',
'仇' => ' qiú',
'叶' => ' yè',
'阚' => ' kàn',
'乐' => ' yuè',
'乜' => ' niè',
'陆' => ' lù',
'殷' => ' yīn',
'牟' => ' móu',
'区' => ' ōu',
'宿' => ' sù',
'俞' => ' yú',
'余' => ' yú',
'齐' => ' qí',
'许' => ' xǔ',
'信' => ' xìn',
'无' => ' wú',
'浣' => ' wǎn',
'艾' => ' ài',
'浅' => ' qiǎn',
'烟' => ' yān',
'蓝' => ' lán',
'於' => ' yú',
'寻' => ' xún',
'殳' => ' shū',
'思' => ' sī',
'鸟' => ' niǎo',
'卜' => ' bǔ',
'单' => ' shàn',
'南' => ' nán',
'柏' => ' bǎi',
'朴' => ' piáo',
'繁' => ' pó',
'曾' => ' zēng',
'瞿' => ' qú',
'缪' => ' miào',
'石' => ' shí',
'冯' => ' féng',
'覃' => ' qín',
'幺' => ' yāo',
'种' => ' chóng',
'折' => ' shè',
'燕' => ' yān',
'纪' => ' jǐ',
'过' => ' guō',
'华' => ' huà',
'冼' => ' xiǎn',
'秘' => ' bì',
'重' => ' chóng',
'解' => ' xiè',
'那' => ' nā',
'和' => ' hé',
'贾' => ' jiǎ',
'塔' => ' tǎ',
'盛' => ' shèng',
'查' => ' zhā',
'盖' => ' gě',
'居' => ' jū',
'哈' => ' hǎ',
'的' => ' dē',
'薄' => ' bó',
'佴' => ' nài',
'六' => ' lù',
'都' => ' dū',
'翟' => ' zhái',
'扎' => ' zā',
'藏' => ' zàng',
'粘' => ' niàn',
'难' => ' nàn',
'若' => ' ruò',
);

8003
vendor/overtrue/pinyin/data/words_0 vendored Normal file

File diff suppressed because it is too large Load Diff

8003
vendor/overtrue/pinyin/data/words_1 vendored Normal file

File diff suppressed because it is too large Load Diff

8003
vendor/overtrue/pinyin/data/words_2 vendored Normal file

File diff suppressed because it is too large Load Diff

8003
vendor/overtrue/pinyin/data/words_3 vendored Normal file

File diff suppressed because it is too large Load Diff

8003
vendor/overtrue/pinyin/data/words_4 vendored Normal file

File diff suppressed because it is too large Load Diff

2056
vendor/overtrue/pinyin/data/words_5 vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the overtrue/pinyin.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Pinyin;
use Closure;
/**
* Dict loader interface.
*/
interface DictLoaderInterface
{
/**
* Load dict.
*
* <pre>
* [
* '响应时间' => "[\t]xiǎng[\t]yìng[\t]shí[\t]jiān",
* '长篇连载' => '[\t]cháng[\t]piān[\t]lián[\t]zǎi',
* //...
* ]
* </pre>
*
* @param Closure $callback
*/
public function map(Closure $callback);
/**
* Load surname dict.
*
* @param Closure $callback
*/
public function mapSurname(Closure $callback);
}

View File

@ -0,0 +1,76 @@
<?php
/*
* This file is part of the overtrue/pinyin.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Pinyin;
use Closure;
/**
* Dict File loader.
*/
class FileDictLoader implements DictLoaderInterface
{
/**
* Words segment name.
*
* @var string
*/
protected $segmentName = 'words_%s';
/**
* Dict path.
*
* @var string
*/
protected $path;
/**
* Constructor.
*
* @param string $path
*/
public function __construct($path)
{
$this->path = $path;
}
/**
* Load dict.
*
* @param Closure $callback
*/
public function map(Closure $callback)
{
for ($i = 0; $i < 100; ++$i) {
$segment = $this->path.'/'.sprintf($this->segmentName, $i);
if (file_exists($segment)) {
$dictionary = (array) include $segment;
$callback($dictionary);
}
}
}
/**
* Load surname dict.
*
* @param Closure $callback
*/
public function mapSurname(Closure $callback)
{
$surnames = $this->path.'/surnames';
if (file_exists($surnames)) {
$dictionary = (array) include $surnames;
$callback($dictionary);
}
}
}

View File

@ -0,0 +1,142 @@
<?php
/*
* This file is part of the overtrue/pinyin.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Pinyin;
use Closure;
use SplFileObject;
use Generator;
/**
* Generator syntax(yield) Dict File loader.
*/
class GeneratorFileDictLoader implements DictLoaderInterface
{
/**
* Data directory.
*
* @var string
*/
protected $path;
/**
* Words segment name.
*
* @var string
*/
protected $segmentName = 'words_%s';
/**
* SplFileObjects.
*
* @var array
*/
protected static $handles = [];
/**
* surnames.
*
* @var SplFileObject
*/
protected static $surnamesHandle;
/**
* Constructor.
*
* @param string $path
*/
public function __construct($path)
{
$this->path = $path;
for ($i = 0; $i < 100; ++$i) {
$segment = $this->path.'/'.sprintf($this->segmentName, $i);
if (file_exists($segment) && is_file($segment)) {
array_push(static::$handles, $this->openFile($segment));
}
}
}
/**
* Construct a new file object.
*
* @param string $filename file path
*
* @return SplFileObject
*/
protected function openFile($filename, $mode = 'r')
{
return new SplFileObject($filename, $mode);
}
/**
* get Generator syntax.
*
* @param array $handles SplFileObjects
*/
protected function getGenerator(array $handles)
{
foreach ($handles as $handle) {
$handle->seek(0);
while ($handle->eof() === false) {
$string = str_replace(['\'', ' ', PHP_EOL, ','], '', $handle->fgets());
if (strpos($string, '=>') === false) {
continue;
}
list($string, $pinyin) = explode('=>', $string);
yield $string => $pinyin;
}
}
}
/**
* Traverse the stream.
*
* @param Generator $generator
* @param Closure $callback
*
* @author Seven Du <shiweidu@outlook.com>
*/
protected function traversing(Generator $generator, Closure $callback)
{
foreach ($generator as $string => $pinyin) {
$callback([$string => $pinyin]);
}
}
/**
* Load dict.
*
* @param Closure $callback
*/
public function map(Closure $callback)
{
$this->traversing($this->getGenerator(static::$handles), $callback);
}
/**
* Load surname dict.
*
* @param Closure $callback
*/
public function mapSurname(Closure $callback)
{
if (!static::$surnamesHandle instanceof SplFileObject) {
static::$surnamesHandle = $this->openFile($this->path.'/surnames');
}
$this->traversing($this->getGenerator([static::$surnamesHandle]), $callback);
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* This file is part of the overtrue/pinyin.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Pinyin;
use Closure;
/**
* Memory Dict File loader.
*/
class MemoryFileDictLoader implements DictLoaderInterface
{
/**
* Data directory.
*
* @var string
*/
protected $path;
/**
* Words segment name.
*
* @var string
*/
protected $segmentName = 'words_%s';
/**
* Segment files.
*
* @var array
*/
protected $segments = array();
/**
* Surname cache.
*
* @var array
*/
protected $surnames = array();
/**
* Constructor.
*
* @param string $path
*/
public function __construct($path)
{
$this->path = $path;
for ($i = 0; $i < 100; ++$i) {
$segment = $path.'/'.sprintf($this->segmentName, $i);
if (file_exists($segment)) {
$this->segments[] = (array) include $segment;
}
}
}
/**
* Load dict.
*
* @param Closure $callback
*/
public function map(Closure $callback)
{
foreach ($this->segments as $dictionary) {
$callback($dictionary);
}
}
/**
* Load surname dict.
*
* @param Closure $callback
*/
public function mapSurname(Closure $callback)
{
if (empty($this->surnames)) {
$surnames = $this->path.'/surnames';
if (file_exists($surnames)) {
$this->surnames = (array) include $surnames;
}
}
$callback($this->surnames);
}
}

309
vendor/overtrue/pinyin/src/Pinyin.php vendored Normal file
View File

@ -0,0 +1,309 @@
<?php
/*
* This file is part of the overtrue/pinyin.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Pinyin;
use InvalidArgumentException;
/*
* Chinese to pinyin translator.
*
* @author overtrue <i@overtrue.me>
* @copyright 2015 overtrue <i@overtrue.me>
*
* @link https://github.com/overtrue/pinyin
* @link http://overtrue.me
*/
define('PINYIN_NONE', 'none');
define('PINYIN_ASCII', 'ascii');
define('PINYIN_UNICODE', 'unicode');
class Pinyin
{
const NONE = 'none';
const ASCII = 'ascii';
const UNICODE = 'unicode';
/**
* Dict loader.
*
* @var \Overtrue\Pinyin\DictLoaderInterface
*/
protected $loader;
/**
* Punctuations map.
*
* @var array
*/
protected $punctuations = array(
'' => ',',
'。' => '.',
'' => '!',
'' => '?',
'' => ':',
'“' => '"',
'”' => '"',
'' => "'",
'' => "'",
);
/**
* Constructor.
*
* @param string $loaderName
*/
public function __construct($loaderName = null)
{
$this->loader = $loaderName ?: 'Overtrue\\Pinyin\\FileDictLoader';
}
/**
* Convert string to pinyin.
*
* @param string $string
* @param string $option
*
* @return array
*/
public function convert($string, $option = self::NONE)
{
$pinyin = $this->romanize($string);
return $this->splitWords($pinyin, $option);
}
/**
* Convert string (person name) to pinyin.
*
* @param string $stringName
* @param string $option
*
* @return array
*/
public function name($stringName, $option = self::NONE)
{
$pinyin = $this->romanize($stringName, true);
return $this->splitWords($pinyin, $option);
}
/**
* Return a pinyin permalink from string.
*
* @param string $string
* @param string $delimiter
*
* @return string
*/
public function permalink($string, $delimiter = '-')
{
if (!in_array($delimiter, array('_', '-', '.', ''), true)) {
throw new InvalidArgumentException("Delimiter must be one of: '_', '-', '', '.'.");
}
return implode($delimiter, $this->convert($string, false));
}
/**
* Return first letters.
*
* @param string $string
* @param string $delimiter
*
* @return string
*/
public function abbr($string, $delimiter = '')
{
return implode($delimiter, array_map(function ($pinyin) {
return $pinyin[0];
}, $this->convert($string, false)));
}
/**
* Chinese phrase to pinyin.
*
* @param string $string
* @param string $delimiter
* @param string $option
*
* @return string
*/
public function phrase($string, $delimiter = ' ', $option = self::NONE)
{
return implode($delimiter, $this->convert($string, $option));
}
/**
* Chinese to pinyin sentense.
*
* @param string $sentence
* @param bool $withTone
*
* @return string
*/
public function sentence($sentence, $withTone = false)
{
$marks = array_keys($this->punctuations);
$punctuationsRegex = preg_quote(implode(array_merge($marks, $this->punctuations)), '/');
$regex = '/[^üāēīōūǖáéíóúǘǎěǐǒǔǚàèìòùǜa-z0-9'.$punctuationsRegex.'\s_]+/iu';
$pinyin = preg_replace($regex, '', $this->romanize($sentence));
$punctuations = array_merge($this->punctuations, array("\t" => ' ', ' ' => ' '));
$pinyin = trim(str_replace(array_keys($punctuations), $punctuations, $pinyin));
return $withTone ? $pinyin : $this->format($pinyin, false);
}
/**
* Loader setter.
*
* @param \Overtrue\Pinyin\DictLoaderInterface $loader
*
* @return $this
*/
public function setLoader(DictLoaderInterface $loader)
{
$this->loader = $loader;
return $this;
}
/**
* Return dict loader,.
*
* @return \Overtrue\Pinyin\DictLoaderInterface
*/
public function getLoader()
{
if (!($this->loader instanceof DictLoaderInterface)) {
$dataDir = dirname(__DIR__).'/data/';
$loaderName = $this->loader;
$this->loader = new $loaderName($dataDir);
}
return $this->loader;
}
/**
* Preprocess.
*
* @param string $string
*
* @return string
*/
protected function prepare($string)
{
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
return "\t".$matches[0];
}, $string);
return preg_replace("~[^\p{Han}\p{P}\p{Z}\p{M}\p{N}\p{L}\t]~u", '', $string);
}
/**
* Convert Chinese to pinyin.
*
* @param string $string
* @param bool $isName
*
* @return string
*/
protected function romanize($string, $isName = false)
{
$string = $this->prepare($string);
$dictLoader = $this->getLoader();
if ($isName) {
$string = $this->convertSurname($string, $dictLoader);
}
$dictLoader->map(function ($dictionary) use (&$string) {
$string = strtr($string, $dictionary);
});
return $string;
}
/**
* Convert Chinese Surname to pinyin.
*
* @param string $string
* @param \Overtrue\Pinyin\DictLoaderInterface $dictLoader
*
* @return string
*/
protected function convertSurname($string, $dictLoader)
{
$dictLoader->mapSurname(function ($dictionary) use (&$string) {
foreach ($dictionary as $surname => $pinyin) {
if (strpos($string, $surname) === 0) {
$string = $pinyin.mb_substr($string, mb_strlen($surname, 'UTF-8'), mb_strlen($string, 'UTF-8') - 1, 'UTF-8');
break;
}
}
});
return $string;
}
/**
* Split pinyin string to words.
*
* @param string $pinyin
* @param string $option
*
* @return array
*/
public function splitWords($pinyin, $option)
{
$split = array_filter(preg_split('/[^üāēīōūǖáéíóúǘǎěǐǒǔǚàèìòùǜa-z\d]+/iu', $pinyin));
if ($option !== self::UNICODE) {
foreach ($split as $index => $pinyin) {
$split[$index] = $this->format($pinyin, $option === self::ASCII);
}
}
return array_values($split);
}
/**
* Format.
*
* @param string $pinyin
* @param bool $tone
*
* @return string
*/
protected function format($pinyin, $tone = false)
{
$replacements = array(
'üē' => array('ue', 1), 'üé' => array('ue', 2), 'üě' => array('ue', 3), 'üè' => array('ue', 4),
'ā' => array('a', 1), 'ē' => array('e', 1), 'ī' => array('i', 1), 'ō' => array('o', 1), 'ū' => array('u', 1), 'ǖ' => array('v', 1),
'á' => array('a', 2), 'é' => array('e', 2), 'í' => array('i', 2), 'ó' => array('o', 2), 'ú' => array('u', 2), 'ǘ' => array('v', 2),
'ǎ' => array('a', 3), 'ě' => array('e', 3), 'ǐ' => array('i', 3), 'ǒ' => array('o', 3), 'ǔ' => array('u', 3), 'ǚ' => array('v', 3),
'à' => array('a', 4), 'è' => array('e', 4), 'ì' => array('i', 4), 'ò' => array('o', 4), 'ù' => array('u', 4), 'ǜ' => array('v', 4),
);
foreach ($replacements as $unicde => $replacement) {
if (false !== strpos($pinyin, $unicde)) {
$pinyin = str_replace($unicde, $replacement[0], $pinyin).($tone ? $replacement[1] : '');
}
}
return $pinyin;
}
}