Commit ec7de05e authored by Davve's avatar Davve

增加tag创建逻辑

parent a612db1c
......@@ -29,4 +29,14 @@ class RegionSearchView(APIView):
class UserSearchView(APIView):
def get(self, request):
pass
\ No newline at end of file
pass
class TagSearchView(APIView):
def get(self, request):
name = request.GET.get('name')
try:
data = self.rpc['venus/community/tag/search'](name=name).unwrap()
except Exception as e:
raise e
return {'data': data}
\ No newline at end of file
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "chenwei"
# Date: 2018/11/17
from utils.base import APIView
class TagListView(APIView):
def get(self, request):
pass
def post(self, request):
pass
class TagUpdateOrCreateView(APIView):
def get(self, request):
pass
def post(self, request):
data = {
'name': request.POST.get('name'),
'level': request.POST.get('level'),
'type': request.POST.get('type'),
'description': request.POST.get('description'),
'down_tag': list(set(request.POST.get('down_tag', '').split(','))),
'up_tag': list(set(request.POST.get('up_tag', '').split(','))),
}
try:
self.rpc['venus/community/tag/update_or_create'](data=data).unwrap()
except Exception as e:
raise e
return {
'message': '操作成功'
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ from .topic import *
from .star import *
from .account import *
from .search import *
from .tag import *
urlpatterns = [
# 登陆,注销相关
......@@ -44,15 +45,21 @@ urlpatterns = [
# pick相关
url(r'pick/list$', PickListView.as_view()),
url(r'pick/list/update$', PickListView.as_view()),
url(r'pick/create$', UpdateOrCreateView.as_view()),
url(r'pick/detail/', UpdateOrCreateView.as_view()),
# 标签相关
url(r'tag/list$', TagListView.as_view()),
url(r'tag/list/update$', TagListView.as_view()),
url(r'tag/create', TagUpdateOrCreateView.as_view()),
url(r'tag/detail', TagUpdateOrCreateView.as_view()),
]
searchurlpatterns = [
url(r'search/group$', GroupSearchView.as_view()),
url(r'search/region', RegionSearchView.as_view()),
url(r'search/user', UserSearchView.as_view()),
url(r'search/tag', TagSearchView.as_view()),
]
urlpatterns += searchurlpatterns
# coding=utf-8
import os
LOG_DIR = '/data/log/sun/app/'
# https://docs.djangoproject.com/en/2.1/topics/logging#configuring-logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(asctime)s %(levelname)s %(module)s.%(funcName)s Line:%(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
'profile': {
'format': '%(asctime)s %(message)s'
},
'raw': {
'format': '%(message)s'
},
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose'
},
'default': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'default.log'),
'formatter': 'verbose',
},
'info_handler': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'info.log'),
'formatter': 'verbose',
},
'error_handler': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'error.log'),
'formatter': 'verbose',
},
'profile_handler': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'profile.log'),
'formatter': 'profile',
},
'exception_handler': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'exception.log'),
'formatter': 'verbose',
},
'tracer_handler': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'tracer.log'),
'formatter': 'raw',
},
},
'loggers': {
'django': {
'handlers': ['default'],
'propagate': True,
'level': 'INFO',
},
'django.request': {
'handlers': ['error_handler'],
'level': 'ERROR',
'propagate': False,
},
'info_logger': {
'handlers': ['info_handler', 'console'],
'level': 'DEBUG',
'propagate': False,
},
'error_logger': {
'handlers': ['error_handler'],
'level': 'ERROR',
'propagate': False,
},
'profile_logger': {
'handlers': ['profile_handler'],
'level': 'INFO',
'propagate': False,
},
'exception_logger': {
'handlers': ['exception_handler'],
'level': 'ERROR',
'propagate': False,
},
'gm_tracer.subscribe': {
'handlers': ['tracer_handler'],
'propagate': False,
'level': 'INFO'
},
}
}
......@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/1.10/ref/settings/
import os
from .settings_local import *
from .log_settings import *
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
......@@ -131,3 +132,9 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
os.path.join(BASE_DIR, 'vu', 'dist', 'static'),
]
# 图片上传
QINIU_ACCESS_KEY = "UPCOYIJkZOMcdd9FDzpBqYjzWUh55fBpVi3AhWpL"
QINIU_SECRET_KEY = "z5YvpDDSam_JE345Z8J_f3TufzelOW2VOGNoBl9e"
QINIU_HOST = "http://wanmeizhensuo.qiniudn.com/"
QINIU_SCOPE = 'wanmeizhensuo'
\ No newline at end of file
var api = require('./api')
//
// const target = 'http://doctor.test.env'
const target = 'http://172.30.8.231:8000'
// const target = 'http://192.168.1.6:8000'
// const target = 'http://172.30.8.231:8000'
const target = 'http://192.168.1.6:8000'
// 可以修改请求内容
const onProxyReq = proxyReq => {}
......
......@@ -23,3 +23,11 @@ export function regionSearch(name) {
params: { name }
})
}
export function tagSearch(name) {
return request({
url: '/api/search/tag',
method: 'get',
params: { name }
})
}
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/api/tag/list',
method: 'get',
params: query
})
}
export function OffLineOrOnLine(data) {
return request({
url: '/api/tag/list/update',
method: 'post',
data
})
}
export function CreateTag(data) {
return request({
url: '/api/tag/create',
method: 'post',
data
})
}
export function fetchTagDetail(id) {
return request({
url: '/api/tag/detail',
method: 'get',
params: { id }
})
}
......@@ -15,6 +15,7 @@ import StarRouter from './modules/star'
import GroupRouter from './modules/group'
import UserRouter from './modules/user'
import AccountRouter from './modules/account'
import TagRouter from './modules/tag'
/** note: Submenu only appear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
......@@ -98,6 +99,7 @@ export const asyncRouterMap = [
StarRouter,
PushRouter,
PickRouter,
TagRouter,
{
path: '/example',
......
import Layout from '@/views/layout/Layout'
const TagRouter = {
path: '/tag',
component: Layout,
redirect: '/tag/list',
name: 'Tag',
meta: {
title: '标签管理',
icon: 'example'
},
children: [
{
path: 'list',
component: () => import('@/views/tag/list'),
name: 'TagList',
meta: {title: '标签列表', icon: 'list'}
},
{
path: 'create',
component: () => import('@/views/tag/create'),
name: 'CreateTag',
meta: {title: '创建标签', icon: 'edit'}
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/tag/edit'),
name: 'EditTag',
meta: {title: '编辑标签', noCache: true},
hidden: true
}
]
}
export default TagRouter
......@@ -3,7 +3,7 @@
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :class-name="'sub-navbar'">
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">发布
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">保存
</el-button>
</sticky>
......@@ -237,8 +237,14 @@
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true;
this.postForm.region = Assembledata(this.temparray['region'], this.postForm.region);
this.postForm.pick_group = Assembledata(this.temparray['pick_group'], this.postForm.pick_group);
if (this.isEdit){
this.postForm.region = Assembledata(this.temparray['region'], this.postForm.region);
this.postForm.pick_group = Assembledata(this.temparray['pick_group'], this.postForm.pick_group);
}else{
console.log(this.postForm.region)
this.postForm.region = this.postForm.region.join(',')
this.postForm.pick_group = this.postForm.pick_group.join(',')
}
CreatePick(this.postForm).then(response => {
this.$notify({
title: '成功',
......
<template>
<div class="createPost-container">
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :class-name="'sub-navbar'">
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">保存
</el-button>
</sticky>
<div class="createPost-main-container">
<el-row>
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="name">
<MDinput v-model="postForm.name" :maxlength="100" name="name" required>
标签名称
</MDinput>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="12">
<el-form-item label-width="75px" label="标签类型:" class="postInfo-container-item" prop="type">
<el-select v-model="postForm.type" :placeholder="'标签类型'" clearable class="postInfo-container-item"
style="width: 300px">
<el-option v-for="item in TagTypeOptions" :key="item.key" :label="item.display_name"
:value="item.key"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label-width="75px" label="标签等级:" class="postInfo-container-item" prop="level">
<el-select v-model="postForm.level" :placeholder="'标签等级'" clearable
class="postInfo-container-item" style="width:300px">
<el-option v-for="item in LevelTypeOptions" :key="item.key" :label="item.display_name"
:value="item.key"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label-width="75px" label="上级标签:" class="postInfo-container-item">
<el-select v-model="postForm.up_tag" :remote-method="getRemoteTagList" filterable remote multiple value-key="id"
placeholder="搜索标签" style="width: 70%">
<el-option v-for="(item,index) in regionListOptions" :key="item+index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label-width="75px" label="下级标签:" class="postInfo-container-item">
<el-select
v-model="postForm.down_tag"
:remote-method="getRemoteTagList"
multiple
filterable
remote
reserve-keyword
placeholder="搜索标签"
style="width: 70%"
:loading="loading"
value-key="id"
>
<el-option v-for="(item, index) in groupListOptions" :key="item+index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="75px" label="标签简介:" prop="desc">
<el-input :rows="1" v-model="postForm.description" type="textarea" class="article-textarea" autosize
placeholder="请输入内容"/>
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}</span>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky' // 粘性header组件
import {fetchTagDetail, CreateTag} from '@/api/tag'
import {tagSearch} from '@/api/remoteSearch'
import { isInArray, removeByvale} from "@/utils";
function Assembledata(target, source) {
var region_data = []
for (var i=0;i<target.length;i++){
if (isInArray(source, target[i]['name'])){
region_data.push(target[i]['id'])
removeByvale(source, target[i]['name']);
}
}
region_data.push(...source)
return region_data.join(',')
}
const defaultForm = {
status: 'draft',
name: '',
level: '',
type: '',
description: '',
up_tag: [],
down_tag: [],
}
export default {
name: 'TagDetail',
components: {MDinput, Sticky},
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + '为必传项',
type: 'error'
})
callback(new Error(rule.field + '为必传项'))
} else {
callback()
}
}
return {
postForm: Object.assign({}, defaultForm),
loading: false,
regionListOptions: [],
groupListOptions: [],
TagTypeOptions: [
{'key': 0, 'display_name': '用户标签'},
{'key': 1, 'display_name': '帖子标签'},
],
LevelTypeOptions: [
{'key': 1, 'display_name': '一级'},
{'key': 2, 'display_name': '二级'},
{'key': 4, 'display_name': '四级'},
{'key': 5, 'display_name': '五级'},
{'key': 6, 'display_name': '六级'},
],
rules: {
name: [{validator: validateRequire, trigger: 'blur'}],
type: [{validator: validateRequire, trigger: 'blur'}],
level: [{validator: validateRequire, trigger: 'blur'}],
},
tempRoute: {},
temparray:{
'up_tag':[],
'down_tag': [],
}
}
},
computed: {
contentShortLength() {
return this.postForm.description.length
}
},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
} else {
this.postForm = Object.assign({}, defaultForm)
}
this.tempRoute = Object.assign({}, this.$route)
},
methods: {
fetchData(id) {
fetchPickDetail(id).then(response => {
// select 回填数据处理,后期再进行优化
let rep = response.data.data.data;
let pick_temp = []
let region_temp = []
for (let i=0;i<rep.pick_group.length;i++){
pick_temp.push(rep.pick_group[i]['name']);
this.temparray['pick_group'].push(rep.pick_group[i])
}
for (let i=0;i<rep.region.length;i++){
region_temp.push(rep.region[i]['name']);
this.temparray['region'].push(rep.region[i])
}
rep.region = region_temp
rep.pick_group = pick_temp
this.postForm = rep
}).catch(err => {
console.log(err)
})
},
submitForm() {
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true;
if (this.isEdit){
this.postForm.up_tag = Assembledata(this.temparray['up_tag'], this.postForm.up_tag);
this.postForm.down_tag = Assembledata(this.temparray['down_tag'], this.postForm.down_tag);
}else{
this.postForm.up_tag = this.postForm.up_tag.join(',')
this.postForm.down_tag = this.postForm.down_tag.join(',')
}
CreateTag(this.postForm).then(response => {
this.$notify({
title: '成功',
message: response.data.data.message,
type: 'success',
duration: 2000
})
setTimeout(() => {
this.$router.push('/tag/list')
}, 1000)
}).catch(err => {
this.$notify({
title: '失败',
message: '操作失败',
type: 'danger',
duration: 2000
})
});
this.postForm.status = 'published'
this.loading = false
} else {
console.log('error submit!!')
return false
}
})
},
getRemoteTagList(query) {
tagSearch(query).then(response => {
if (!response.data.data.data) return
this.regionListOptions = response.data.data.data
})
},
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
.editor-container {
min-height: 500px;
margin: 0 0 30px;
.editor-upload-btn-container {
text-align: right;
margin-right: 10px;
.editor-upload-btn {
display: inline-block;
}
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: -10px;
top: 0px;
}
}
</style>
<template>
<tag-detail :is-edit="false"/>
</template>
<script>
import TagDetail from './components/TagDetail'
export default {
name: 'EditTag',
components: { TagDetail }
}
</script>
<template>
<tag-detail :is-edit="true"/>
</template>
<script>
import TagDetail from './components/TagDetail'
export default {
name: 'EditTag',
components: { TagDetail }
}
</script>
<template>
<div class="app-container">
<div class="filter-container">
<el-input :placeholder="'搜素'" v-model="listQuery.filter.value" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter"/>
<el-select v-model="listQuery.filter.key" :placeholder="'搜索字段'" clearable class="filter-item" style="width: 110px">
<el-option v-for="item in SearchTypeOptions" :key="item.key" :label="item.display_name" :value="item.key"/>
</el-select>
<el-select v-model="listQuery.filter.is_online" :placeholder="'上线'" clearable class="filter-item" style="width: 100px">
<el-option v-for="item in BooleanTypeOptions" :key="item.key" :label="item.display_name" :value="item.key"/>
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">搜索</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">创建</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleOfflineOrOnline('offline')">下线</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleOfflineOrOnline('online')">上线</el-button>
</div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%" ref="multipleTable" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column align="center" label="pickID" width="80">
<template slot-scope="scope">
<router-link :to="'/pick/edit/'+scope.row.id" class="link-type">
<span>{{ scope.row.id }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="pick名称">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column width="280px" align="center" label="pick简介">
<template slot-scope="scope">
<span>{{ scope.row.desc }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="pick用户">
<template slot-scope="scope">
<span>{{ scope.row.user_nums }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="pick小组数">
<template slot-scope="scope">
<span>{{ scope.row.pick_group_nums }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="pick属性">
<template slot-scope="scope">
<span>{{ scope.row.pick_type }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="性别">
<template slot-scope="scope">
<el-tag :type="scope.row.gender | genderFilter">{{scope.row.gender}}</el-tag>
</template>
</el-table-column>
<el-table-column width="130px" align="center" label="地区">
<template slot-scope="scope">
<span>{{ scope.row.region }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="下线">
<template slot-scope="scope">
<el-tag :type="scope.row.is_online | isOnlineFilter">{{ scope.row.is_online==1 ? '是' : '否' }}</el-tag>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" style="margin-left: 150px;" @pagination="getList" />
</div>
</template>
<script>
import { fetchList, OffLineOrOnLine } from '@/api/pick'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import waves from '@/directive/waves'
export default {
name: 'TagList',
components: { Pagination },
filters: {
isOnlineFilter(status) {
const statusMap = {
1: 'success',
0: 'info',
}
return statusMap[status]
},
genderFilter(status) {
const statusMap = {
'男': 'success',
'女': 'info',
'全部': 'danger'
}
return statusMap[status]
},
},
directives: { waves },
data() {
return {
list: null,
total: 0,
listLoading: true,
multipleSelection: [],
del_list: [],
listQuery: {
page: 1,
limit: 10,
filter: {
value: '',
key: '',
is_online: ''
},
},
BooleanTypeOptions: [
{'key': 1, 'display_name': '是'},
{'key': 0, 'display_name': '否'}
],
SearchTypeOptions:[
{'key': 'id', 'display_name': 'ID'},
{'key': 'name', 'display_name': 'pick名称'},
]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.data.data
this.total = response.data.data.total
this.listLoading = false
})
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
handleOfflineOrOnline(val){
const length = this.multipleSelection.length;
let str = '';
this.del_list = this.del_list.concat(this.multipleSelection);
for (let i = 0; i < length; i++) {
if (val === 'offline'){
this.multipleSelection[i].is_online = 0
}else{
this.multipleSelection[i].is_online = 1
}
str += this.multipleSelection[i].id + ' ';
}
OffLineOrOnLine({type:val, ids:str}).then(response => {
this.multipleSelection = [];
response.data.data.message
this.$message.success(response.data.data.message);
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleCreate() {
this.$router.push('/pick/create')
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>
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