feat: remove code generator module (view pages and api)

This commit is contained in:
张家训
2026-08-21 14:25:24 +08:00
parent 34f892c04e
commit ec2fc973a0
4 changed files with 1 additions and 787 deletions
+1 -6
View File
@@ -1,6 +1,6 @@
# Freedom Web
基于 **Vue 2 + view-design(iView 4)** 的后台管理前端,配套 [freedom-admin](https://gitlab.jiaxun.site/yiciheng/freedom-admin.git) 后端服务,支持动态路由菜单、权限控制与代码生成。
基于 **Vue 2 + view-design(iView 4)** 的后台管理前端,配套 [freedom-admin](https://gitlab.jiaxun.site/yiciheng/freedom-admin.git) 后端服务,支持动态路由菜单与权限控制。
## 技术栈
@@ -22,7 +22,6 @@ freedom-web
├── src/
│ ├── api/ # 接口请求层
│ │ ├── sys/ # 系统管理(用户/角色/部门/权限)
│ │ ├── code/ # 代码生成
│ │ ├── basic/ # 基础配置
│ │ └── kpi/ # KPI 指标模块
│ ├── assets/ # 静态资源(图标/图片)
@@ -35,7 +34,6 @@ freedom-web
│ │ ├── login/ # 登录页
│ │ ├── home/ # 首页/个人中心/消息中心
│ │ ├── sys/ # 系统管理(用户/角色/部门/权限)
│ │ ├── code/ # 代码生成(配置 + 生成器)
│ │ └── kpi/ # KPI 指标模块
│ └── main.js # 应用入口
└── vue.config.js # 构建配置
@@ -50,9 +48,6 @@ freedom-web
- 角色管理
- 部门管理(树形)
- 权限管理
- **代码生成**
- 研发中心-代码生成全局配置(项目路径 / 包名 / 模板选择)
- 代码生成器(读取数据库表,一键生成前后端代码)
- **KPI 指标模块**:指标库、员工指标、模板管理、指标记录等
- **通用能力**:tag 标签导航、面包屑、全屏、锁屏、个人中心、消息中心、错误页(401/403/404/500)
-38
View File
@@ -1,38 +0,0 @@
import axios from '@/libs/api.request'
import {getToken} from "@/libs/util"
/** 获取列表 **/
export const getDataList = (data) => {
return axios.request({
url: 'generator/findListWithPage',
headers: {
"Authorization": getToken()
},
data: data,
method: 'post'
})
}
/** 获取table下拉列表 **/
export const getTableSelectList = (data) => {
return axios.request({
url: 'generator/getTableSelectList',
headers: {
"Authorization": getToken()
},
method: 'post'
})
}
/** 提交代码生成规则 **/
export const executeGenerator = (data) => {
return axios.request({
url: 'generator/execute',
headers: {
"Authorization": getToken()
},
data: data,
method: 'post'
})
}
-268
View File
@@ -1,268 +0,0 @@
<template>
<Tabs>
<TabPane label="研发中心-代码生成全局配置" icon="ios-cloud-circle">
<Form :rules="generatorRuleCustom" :model="generatorFormItem" ref="generatorFormItem" :label-width="150"
label-position="right">
<Row :gutter="24">
<Col span="12">
<FormItem label="frontProjectDir" prop="frontProjectDir">
<Input type="text" class="form-input" v-model="generatorFormItem.frontProjectDir"
placeholder="frontProjectDir"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="afterProjectDir" prop="afterProjectDir">
<Input type="text" class="form-input" v-model="generatorFormItem.afterProjectDir"
placeholder="afterProjectDir"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="mapperPackage" prop="mapperPackage">
<Input type="text" class="form-input" v-model="generatorFormItem.mapperPackage"
placeholder="mapperPackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="entityPackage" prop="entityPackage">
<Input type="text" class="form-input" v-model="generatorFormItem.entityPackage"
placeholder="entityPackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="servicePackage" prop="servicePackage">
<Input type="text" class="form-input" v-model="generatorFormItem.servicePackage"
placeholder="servicePackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="serviceImplPackage" prop="serviceImplPackage">
<Input type="text" class="form-input" v-model="generatorFormItem.serviceImplPackage"
placeholder="serviceImplPackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="controllerPackage" prop="controllerPackage">
<Input type="text" class="form-input" v-model="generatorFormItem.controllerPackage"
placeholder="controllerPackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="basePackage" prop="basePackage">
<Input type="text" class="form-input" v-model="generatorFormItem.basePackage" placeholder="basePackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="configPackage" prop="configPackage">
<Input type="text" class="form-input" v-model="generatorFormItem.configPackage"
placeholder="configPackage"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="author" prop="author">
<Input type="text" class="form-input" v-model="generatorFormItem.author" placeholder="author"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="date" prop="date">
<Input type="text" class="form-input" v-model="generatorFormItem.date" placeholder="date"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="view上级目录" prop="viewParentDir">
<Input type="text" class="form-input" v-model="generatorFormItem.viewParentDir"
placeholder="viewParentDir"/>
</FormItem>
</Col>
<Col span="24">
<FormItem label="使用template" prop="usingTemplate">
<CheckboxGroup v-model="generatorFormItem.usingTemplate">
<Checkbox label="apiJsTemplate">
<Icon type="md-pricetag" />
<span>.js</span>
</Checkbox>
<Checkbox label="viewTemplate">
<Icon type="md-pricetag" />
<span>.vue</span>
</Checkbox>
<Checkbox label="mapperXmlTemplate">
<Icon type="md-pricetag" />
<span>mapper.xml</span>
</Checkbox>
<Checkbox label="mapperTemplate">
<Icon type="md-pricetag" />
<span>mapper.java</span>
</Checkbox>
<Checkbox label="entityTemplate">
<Icon type="md-pricetag" />
<span>entity.java</span>
</Checkbox>
<Checkbox label="serviceTemplate">
<Icon type="md-pricetag" />
<span>service.java</span>
</Checkbox>
<Checkbox label="serviceImplTemplate">
<Icon type="md-pricetag" />
<span>serviceImpl.java</span>
</Checkbox>
<Checkbox label="controllerTemplate">
<Icon type="md-pricetag" />
<span>controller.java</span>
</Checkbox>
</CheckboxGroup>
</FormItem>
</Col>
<Col span="12">
<FormItem>
<Button type="primary" @click="handleGeneratorConfigSubmit('generatorFormItem')">保存并生效</Button>
</FormItem>
</Col>
</Row>
</Form>
</TabPane>
</Tabs>
</template>
<script>
import {findConfig, update} from '@/api/basic/basic-config'
import {objectUtil, utils, exportExcel} from "@/common/js/util";
export default {
name: "generator-config",
data() {
return {
indeterminate: true,
checkAll: false,
generatorFormItem: {
frontProjectDir: '',
afterProjectDir: '',
mapperPackage: '',
entityPackage: '',
servicePackage: '',
serviceImplPackage: '',
controllerPackage: '',
basePackage: '',
configPackage: '',
author: '',
date: '',
viewParentDir: '',
className: '',
classNameLowerCase: '',
resourceName: '',
usingTemplate: []
},
generatorRuleCustom: {
frontProjectDir: [{
required: true, message: '请输入前端项目所在系统路径', trigger: 'blur'
}],
afterProjectDir: [{
required: true, message: '请输入后端项目所在系统路径', trigger: 'blur'
}],
mapperPackage: [{
required: true, message: '请输入mapper文件生成路径', trigger: 'blur'
}],
entityPackage: [{
required: true, message: '请输入entity文件生成路径', trigger: 'blur'
}],
servicePackage: [{
required: true, message: '请输入service文件生成路径', trigger: 'blur'
}],
serviceImplPackage: [{
required: true, message: '请输入serviceImpl文件生成路径', trigger: 'blur'
}],
controllerPackage: [{
required: true, message: '请输入controller文件生成路径', trigger: 'blur'
}],
basePackage: [{
required: true, message: '请输入通用mapper文件路径', trigger: 'blur'
}],
configPackage: [{
required: true, message: '请输入config文件路径', trigger: 'blur'
}],
author: [{
required: true, message: '请输入author作者名称', trigger: 'blur'
}],
date: [{
required: true, message: '请输入date日期', trigger: 'blur'
}],
viewParentDir: [{
required: true, message: '请输入viewParentDir描述', trigger: 'blur'
}],
},
}
},
methods: {
findGeneratorConfig() {
let data = {
configType: 'generator_config'
}
findConfig(data).then(res => {
let configValue = res.data.data.entity.configValue
let temp = JSON.parse(configValue)
console.log(configValue)
this.generatorFormItem.frontProjectDir = temp.frontProjectDir
this.generatorFormItem.afterProjectDir = temp.afterProjectDir
this.generatorFormItem.mapperPackage = temp.mapperPackage
this.generatorFormItem.entityPackage = temp.entityPackage
this.generatorFormItem.servicePackage = temp.servicePackage
this.generatorFormItem.serviceImplPackage = temp.serviceImplPackage
this.generatorFormItem.controllerPackage = temp.controllerPackage
this.generatorFormItem.basePackage = temp.basePackage
this.generatorFormItem.configPackage = temp.configPackage
this.generatorFormItem.author = temp.author
this.generatorFormItem.date = temp.date
this.generatorFormItem.viewParentDir = temp.viewParentDir
if (temp.usingTemplate != null){
this.generatorFormItem.usingTemplate = temp.usingTemplate.split(',')
}
})
},
handleGeneratorConfigSubmit(value) {
this.$refs[value].validate((valid) => {
if (valid) {
let configValue = {
frontProjectDir: this.generatorFormItem.frontProjectDir,
afterProjectDir: this.generatorFormItem.afterProjectDir,
mapperPackage: this.generatorFormItem.mapperPackage,
entityPackage: this.generatorFormItem.entityPackage,
servicePackage: this.generatorFormItem.servicePackage,
serviceImplPackage: this.generatorFormItem.serviceImplPackage,
controllerPackage: this.generatorFormItem.controllerPackage,
basePackage: this.generatorFormItem.basePackage,
configPackage: this.generatorFormItem.configPackage,
author: this.generatorFormItem.author,
date: this.generatorFormItem.date,
viewParentDir: this.generatorFormItem.viewParentDir,
usingTemplate: this.generatorFormItem.usingTemplate.toString()
}
let data = {
configType: 'generator_config',
configValue: JSON.stringify(configValue).toString(),
status: '1'
}
update(data).then(res => {
this.$Message.success('更新配置文件成功!');
})
} else {
this.$Message.error('请将配置信息填写完整!');
}
})
},
},
created() {
},
mounted() {
this.findGeneratorConfig()
}
}
</script>
<style lang="less" scoped>
.form-input {
width: 400px
}
</style>
-475
View File
@@ -1,475 +0,0 @@
<template>
<div>
<!-- 查询区域 start -->
<div class="option">
<Select class="short-input" v-model="search.yTableName" placeholder="数据库表名" @on-change="getDataList">
<Option v-for="item in yTableList"
:value="item.value"
:key="item.value">{{ item.label }}
</Option>
</Select>
<Button class="free" type="primary" icon="ios-search" @click="getDataList()">查询</Button>
<Button class="free" type="primary" icon="md-refresh" @click="refresh()">重置</Button>
<Button class="free" style="position: absolute;right: 20px;margin: 5px 0 0 0" icon="md-hammer"
@click="toGenerator()">进入代码生成模式
</Button>
</div>
<!-- 查询区域 end -->
<!-- 列表区域 start -->
<Table :height="fixedHeader ? autoHeight : ''" border ref="table" :columns="listColumns" :data="dataList"
:loading="tableLoading">
</Table>
<div class="pagination">
<Page show-elevator show-sizer show-total :total="pageTotal" :page-size="pageParam.pageSize"
:current="pageParam.pageNum"
@on-page-size-change="changePageSize" @on-change="changePage"></Page>
</div>
<!-- 列表区域 end -->
<!-- generatorModal区域 start -->
<Modal v-model="generatorModal" :title="generatorModalTitle" fullscreen :mask-closable="false"
@on-cancel="cancel()">
<Table @on-selection-change="handleSelectRow()" border ref="generatorTable" :columns="generatorListColumns" :data="generatorDataList">
<template slot-scope="{ row, index }" slot="gVariableDefault">
<Input type="text" v-model="generatorDataList[index].gVariableDefault" />
</template>
<template slot-scope="{ row, index }" slot="gVariableComment">
<Input type="text" v-model="generatorDataList[index].gVariableComment" />
</template>
<template slot-scope="{ row, index }" slot="gVariableIsQuery">
<Checkbox v-model="generatorDataList[index].gVariableIsQuery">是</Checkbox>
</template>
<template slot-scope="{ row, index }" slot="gVariableIsList">
<Checkbox v-model="generatorDataList[index].gVariableIsList">是</Checkbox>
</template>
<template slot-scope="{ row, index }" slot="gVariableIsFormItem">
<Checkbox v-model="generatorDataList[index].gVariableIsFormItem">是</Checkbox>
</template>
<template slot-scope="{ row, index }" slot="gVariableIsCheck">
<Checkbox v-model="generatorDataList[index].gVariableIsCheck">是</Checkbox>
</template>
</Table>
<div slot="footer">
<Button type="text" @click="cancel">取消</Button>
<Button type="primary"
:loading="submitLoading"
@click="submit()">执行生成代码
</Button>
</div>
</Modal>
<!-- generatorModal区域 end -->
<!-- generatorResultModal区域 start -->
<Modal v-model="generatorResultModal" title="执行结果" width="600" :mask-closable="false">
<Alert type="success" show-icon v-for="(val, key, i) in generatorResult">
{{ key }}
<span slot="desc"> {{ val }} </span>
</Alert>
<div slot="footer">
<Button type="primary"
@click="generatorResultModal = false">我已知晓
</Button>
</div>
</Modal>
<!-- generatorResultModal区域 end -->
</div>
</template>
<script>
import { getDataList, getTableSelectList, executeGenerator } from '@/api/code/youth-generator'
import { objectUtil } from "@/common/js/util"
export default {
name: "youth-generator",
data() {
return {
fixedHeader: true, //高度自适应
autoHeight: `${window.innerHeight} ` * 0.68,//获取当前可视高度-定义Table高度
search: {
yTableName: ''
},
tableLoading: false,
yTableList: [], //数据库列表
dataList: [],
pageTotal: 0, //总页数
pageSize: 10, //每页显示条数
pageCurrent: 1, //当前页
pageParam: { //调用时候的分页参数
pageNum: 1,
pageSize: 20
},
listColumns: [
{
title: "#",
type: 'index',
width: 60,
align: 'center',
indexMethod: (row) => {
return (row._index + 1) + ((this.pageParam.pageNum - 1) * this.pageParam.pageSize);
},
},
{
title: '字段名',
key: 'yColumnName',
minWidth: 150,
},
{
title: '数据类型',
key: 'yColumnType',
width: 150,
},
{
title: '字段长度',
key: 'yCharacterMaximumLength',
width: 120,
},
{
title: '是否主键',
key: 'yIsColumnKey',
width: 120,
},
{
title: '是否为空',
key: 'yIsNullable',
width: 120,
},
{
title: '默认值',
key: 'yColumnDefault',
width: 120,
},
{
title: '注释',
key: 'yColumnComment',
minWidth: 200,
},
],
generatorModal: false,
generatorModalTitle: '',
submitLoading: false,
//代码生成模式 start
selectionList: [],
generatorListColumns: [
{
type: 'selection',
width: 60,
align: 'center'
},
{
title: "#",
type: 'index',
width: 60,
align: 'center',
indexMethod: (row) => {
return (row._index + 1) + ((this.pageParam.pageNum - 1) * this.pageParam.pageSize);
},
},
{
title: 'MySQL字段名',
key: 'yColumnName',
width: 150,
},
{
title: 'MySQL类型',
key: 'yColumnType',
width: 120,
},
{
title: 'Java变量名称',
key: 'gVariableName',
width: 150,
},
{
title: 'Java变量类型',
key: 'gVariableType',
width: 130,
},
{
title: 'ID',
key: 'gVariableIsId',
width: 70,
},
{
title: '默认值',
slot: 'gVariableDefault',
width: 120,
},
{
title: '注释',
slot: 'gVariableComment',
minWidth: 150,
},
{
title: '是否查询',
slot: 'gVariableIsQuery',
width: 110,
},
{
title: '是否列表',
slot: 'gVariableIsList',
width: 100,
},
{
title: '是否Form',
slot: 'gVariableIsFormItem',
width: 100,
},
{
title: '是否校验',
slot: 'gVariableIsCheck',
width: 100,
},
{
title: '校验类型',
key: 'gVariableCheckType',
width: 100,
},
],
generatorDataList: [],
generatorResultModal: false,
generatorResult: {},
//代码生成模式 end
}
},
methods: {
getDataList() {
let temp = JSON.parse(JSON.stringify(this.pageParam))
let data = objectUtil.trimJoin(temp, this.search)
this.tableLoading = true
getDataList(data).then(res => {
let pageInfo = res.data.data.pageInfo || res.data.data
this.pageTotal = pageInfo.total
this.dataList = pageInfo.list
// 延迟300毫秒,给它更流畅的感觉
setTimeout(() => {
this.tableLoading = false
}, 300)
})
},
handleSelectRow() {
this.selectionList = this.$refs.generatorTable.getSelection()
console.log(this.selectionList)
},
toGenerator() {
if (this.yTableList.length === 0) {
return this.$Message.warning("未检测到数据库,无法进入代码生成模式!")
} else if (this.dataList.length === 0) {
return this.$Message.warning("未检测到当前数据库字段,无法进入代码生成模式!")
}
for (let i = 0; i < this.yTableList.length; i++) {
if (this.yTableList[i].value === this.search.yTableName) {
this.generatorModalTitle = '代码生成模式,目标数据库:' + this.yTableList[i].label
break
}
}
let tempDataList = []
for (let i = 0; i < this.dataList.length; i++) {
tempDataList[i] = this.dataList[i]
// mysql基本数据类型与java对照转化
if (this.dataList[i].yDataType === 'int') {
tempDataList[i].gVariableType = 'Integer'
} else if (this.dataList[i].yDataType === 'float') {
tempDataList[i].gVariableType = 'Float'
} else if (this.dataList[i].yDataType === 'double' || this.dataList[i].yDataType === 'decimal') {
tempDataList[i].gVariableType = 'Double'
} else if (this.dataList[i].yDataType === 'char') {
tempDataList[i].gVariableType = 'char'
} else if (this.dataList[i].yDataType === 'varchar') {
tempDataList[i].gVariableType = 'String'
} else if (this.dataList[i].yDataType === 'datetime') {
tempDataList[i].gVariableType = 'Date'
}
// mysql基本数据类型与JdbcType对照转化
if (this.dataList[i].yDataType === 'int') {
tempDataList[i].gVariableJdbcType = 'INTEGER'
} else if (this.dataList[i].yDataType === 'float') {
tempDataList[i].gVariableJdbcType = 'FLOAT'
} else if (this.dataList[i].yDataType === 'double' || this.dataList[i].yDataType === 'decimal') {
tempDataList[i].gVariableJdbcType = 'DOUBLE'
} else if (this.dataList[i].yDataType === 'char') {
tempDataList[i].gVariableJdbcType = 'CHAR'
} else if (this.dataList[i].yDataType === 'varchar') {
tempDataList[i].gVariableJdbcType = 'VARCHAR'
} else if (this.dataList[i].yDataType === 'datetime') {
tempDataList[i].gVariableJdbcType = 'DATE'
}
//是否为Id
tempDataList[i].gVariableIsId = this.dataList[i].yIsColumnKey
//默认值
tempDataList[i].gVariableDefault = this.dataList[i].yColumnDefault
if(this.dataList[i].yColumnName === 'status'){
tempDataList[i].gVariableDefault = '1'
}
//注释
tempDataList[i].gVariableComment = this.dataList[i].yColumnComment
//mysql字段转化为小驼峰
tempDataList[i].gVariableName = this.toHump(this.dataList[i].yColumnName)
tempDataList[i].gVariableToCamel = this.toCamel(this.dataList[i].yColumnName)
//设置是否查询
if (this.dataList[i].yColumnName === 'id'
|| this.dataList[i].yColumnName === 'name'
|| this.dataList[i].yColumnName === 'status'){
tempDataList[i].gVariableIsQuery = true
}else {
tempDataList[i].gVariableIsQuery = false
}
//设置是否列表
if (this.dataList[i].yColumnName === 'id'
|| this.dataList[i].yColumnName === 'add_user'
|| this.dataList[i].yColumnName === 'add_time'
|| this.dataList[i].yColumnName === 'edit_user'
|| this.dataList[i].yColumnName === 'edit_time'
|| this.dataList[i].yColumnName === 'status'){
tempDataList[i].gVariableIsList = false
}else {
tempDataList[i].gVariableIsList = true
}
//设置是否FormItem
if (this.dataList[i].yColumnName === 'add_user'
|| this.dataList[i].yColumnName === 'add_time'
|| this.dataList[i].yColumnName === 'edit_user'
|| this.dataList[i].yColumnName === 'edit_time'){
tempDataList[i].gVariableIsFormItem = false
}else {
tempDataList[i].gVariableIsFormItem = true
}
//设置是否校验
if (this.dataList[i].yColumnName === 'id'
|| this.dataList[i].yColumnName === 'add_user'
|| this.dataList[i].yColumnName === 'add_time'
|| this.dataList[i].yColumnName === 'edit_user'
|| this.dataList[i].yColumnName === 'edit_time'
|| this.dataList[i].yColumnName === 'status'){
tempDataList[i].gVariableIsCheck = false
}else {
tempDataList[i].gVariableIsCheck = true
}
//设置校验类型
if (this.dataList[i].yDataType === 'int'
|| this.dataList[i].yDataType === 'float'
|| this.dataList[i].yDataType === 'double'
|| this.dataList[i].yDataType === 'decimal') {
tempDataList[i].gVariableCheckType = 'number'
} else if (this.dataList[i].yDataType === 'char' || this.dataList[i].yDataType === 'varchar') {
tempDataList[i].gVariableCheckType = 'string'
} else if (this.dataList[i].yDataType === 'datetime') {
tempDataList[i].gVariableCheckType = 'date'
}
}
this.generatorDataList = tempDataList
this.generatorModal = true;
},
submit() {
if (this.selectionList.length === 0){
return this.$Message.warning("请选择字段!")
}
this.submitLoading = true
// 提交代码生成规则
let data = {
tableName: this.search.yTableName,
ruleStr: JSON.stringify(this.selectionList).toString()
}
executeGenerator(data).then(res => {
this.generatorResult = res.data.data.result
console.log(this.generatorResult)
this.generatorResultModal = true
this.submitLoading = false
this.generatorModal = false
})
},
cancel() {
this.submitLoading = false;
this.generatorModal = false;
},
refresh() {
this.pageParam = {
pageNum: 1,
pageSize: 10
};
this.search = {
yTableName: this.search.yTableName,
};
},
changePage(index) {
// 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1)
this.pageParam.pageNum = index;
this.getDataList();
},
changePageSize(page) {
// 切换分页条数
this.pageParam.pageNum = 1;
this.pageParam.pageSize = page;
this.getDataList();
},
getTableSelectList() {
getTableSelectList().then(res => {
const data = res.data.data
this.yTableList = Array.isArray(data) ? data : ((data && data.list) || [])
if (this.yTableList.length > 0) {
this.search.yTableName = this.yTableList[0].value
this.getDataList()
}
})
},
// 下划线转小驼峰
toHump(value) {
return value.replace(/\_(\w)/g, function (all, letter) {
return letter.toUpperCase()
})
},
// 下划线转大驼峰
toCamel(value){
return value.replace(/(^|_)(\w)/g,(m,$1,$2)=>$2.toUpperCase());
},
// 驼峰转换下划线
toLine(value) {
return value.replace(/([A-Z])/g, "_$1").toLowerCase()
}
},
created() {
},
mounted() {
this.getTableSelectList()
}
}
</script>
<style lang="less" scoped>
.short-input {
width: 200px;
}
.option {
line-height: 3;
padding-bottom: 10px;
border-bottom: 1px solid #ddd;
}
.option .short-input {
width: 200px;
margin-right: 10px;
}
.option .free {
margin-left: 10px;
}
.form-input {
width: 280px
}
.form-textarea {
width: 730px;
}
</style>