Instantly share code, notes, and snippets.

Embed
What would you like to do?
Oratta Framework Vue Components
<?php
/**
* VueJsコンポーネントセット
*
* id="vueEnabled" なタグの内側にコンポーネントの呼び出しタグを書いて
* vue.ready = true すると使うことができる
*
* ECMAScript 6準拠で書いているのでProductionには投入できないと思われる
* (PhpStorm > Preference > Langage > Javascript > ECMAScript 6にするとIDE上のエラーを抑制できる)
*
* TODO: コンポーネント部分を別のファイルに分けてもいいかも?でも各コンポーネントが密結合なんだよなぁ…
*
* @var String $bordercolor
*/
?>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="//unpkg.com/element-ui/lib/umd/locale/ja.js"></script>
<script>
/**
* v-table
* @var Object[] :data 表示するコンテンツを保持する配列
* @var mixied[] :struct 構造定義配列
*
* [:structの仕様]
* 配列:横方向にセルを作る
* 配列→配列:縦方向に行を作る
* オブジェクト:セルの中身の定義
* class tdにクラスを付与する
* colspan tdにcolspanを付与する
* rowspan tdにrowspanを付与する
* align tdにalignを付与する
*
* text テキストを表示する
* eText テキストを表示する(evalされる)
* enum enumsから指定のキーを使って指定の値があれば文字列に置き換える
* img パスの画像を表示する(evalされる)
* 画像パスの配列になるキーを渡すと、
* 画像の下に切り替え用のカーソルが表示されて順繰りに切り替えられる
* modal trueにするとクリックで拡大
* width
* height
*
* slotStructName,slot セルの中に表を作る。
* slotStructNameはv-tableのstructのためにevalされ、slotはv-tableのdataのためにevalされる
*
* evalされるものについては object.[key] で値を表示できる
* (定数を渡したいときは"'hoge'" のように1つ多くクオートすることで渡す
*/
Vue.component('v-table', {
props: ['data', 'struct', 'width'],
template: `<table class="v-table" :width=width v-if="data.length > 0"><v-tr v-for="(d, index) in data" :d="d" :index="index" :struct="struct" :key=index :width=width /></table>`
});
/* 横方向ループ */
Vue.component('v-tr', {
props: ['d', "struct", 'width'],
template: `<tr><v-td v-for="(s, k) in struct" :s=s :d=d :key=k :width=width /></tr>`
});
/* インライン縦方向ループ */
Vue.component('v-inline-tr', {
props: ['d', "struct", 'width'],
/* tr が重複してしまうのはバグにしてvueの仕様 */
template: `<tr>
<v-td v-for="(s, k) in struct" :s=s :d=d :key=k :width=width />
</tr>`
});
/* セル内処理 */
Vue.component('v-td', {
props: ['s', 'd', 'width'],
data: function () {
return {
enums: vue.enums
}
},
template: `
<v-inline-tr v-if="Array.isArray(s)" :struct=s :d=d :width=width />
<td v-else-if="s.hasOwnProperty('slotStructName')"
:colspan="s.hasOwnProperty('colspan') ? s.colspan : null"
:rowspan="s.hasOwnProperty('rowspan') ? s.rowspan : null"
:class="s.hasOwnProperty('class') ? s.class : null"
:align="s.hasOwnProperty('align') ? s.align : null">
<v-table v-if="$eval(s.slot)" :data="$eval(s.slot)" :struct="$eval(s.slotStructName)"
width="100%" :class="s.hasOwnProperty('class') ? s.class : null" />
</td>
<td v-else :colspan="s.hasOwnProperty('colspan') ? s.colspan : null"
:rowspan="s.hasOwnProperty('rowspan') ? s.rowspan : null"
:class="s.hasOwnProperty('class') ? s.class : null"
:align="s.hasOwnProperty('align') ? s.align : null">
<template v-if="s.hasOwnProperty('img')">
<imgs
:srces="!Array.isArray($eval(s.img)) ? [$eval(s.img)] : $eval(s.img)"
:modalable="s.hasOwnProperty('modal') ? true : false"
:width="s.hasOwnProperty('width') ? s.width : null"
:height="s.hasOwnProperty('height') ? s.height : null" />
</template>
<template v-else-if="s.hasOwnProperty('text')">
<span>{{s.text}}</span>
</template>
<template v-else-if="s.hasOwnProperty('eText')">
<span v-if="!s.hasOwnProperty('enum')">{{$eval(s.eText)}}</span>
<span v-else-if="!enums[s.enum]">{{s.enum}}がenumsに登録されていません</span>
<span v-else-if="enums[s.enum][$eval(s.eText)]">{{enums[s.enum][$eval(s.eText)]}}</span>
<span v-else>{{$eval(s.eText)}}</span>
</template>
<template v-else-if="s.hasOwnProperty('present')">
<present-button :present="$eval(s.present)" :content="$eval(s.content)"></present-button>
</template>
<template v-else>オブジェクトパースエラー</template>
</td>
`, methods: {
'$eval'(expr) {
const object = this.d;
/* console.log([expr, eval(expr)]); */
return eval(expr)
}
},
});
/**
* 拡張imgタグ
* srces 配列で画像のパスを受け取る
* クリックでモーダルを開き大きく表示
*
* @var String width
* @var String height
*/
Vue.component('imgs', {
props: ['srces', 'width', 'height', 'modalable'],
data: function () {
return {
index: 0
}
},
template: `
<div>
<img :src="srces[index]" @click="modal(srces[index])"
:width="width ? width : null" :height="height ? height : null" />
<div v-if="srces.length > 1">
<i class="el-icon-d-arrow-left" @click="prev" style="float:left; font-size:16px"></i>
<i class="el-icon-d-arrow-right" @click="next" style="float:right; font-size:16px"></i>
</div>
</div>
`,
methods: {
modal(src) {
if (!this.modal) {
return;
}
this.$msgbox({
message: `<img src="${src}" class="modalImage">`,
title: '',
dangerouslyUseHTMLString: true,
showClose: false,
lockScroll: false
});
},
next() {
if (this.srces.length - 1 <= this.index) {
this.index = 0;
return;
}
this.index++;
},
prev() {
if (this.index === 0) {
this.index = this.srces.length - 1;
return;
}
this.index--;
}
}
});
/**
* @var String present プレゼントテキスト 末尾に :1 が付与された状態でプロンプトの初期値が表示される
* @var String content プロンプトに表示する文章
*/
Vue.component('presentButton', {
props: ['present', 'content'],
template: `
<input type="button" @click="presentExec" value="付与する" />
`,
methods: {
presentExec() {
this.$prompt(this.content, {
title: 'プレゼント付与',
inputValue: `${this.present}:1`
}).then((value) => {
$.post('present_exec', {'text': value.value}).success((res) => {
this.$message('実行しました');
})
});
}
}
});
/**
* セレクトボックス生成
* @var String name selectボックスのname
* @var Object select キーをvalue, 値を表示文字とする選択肢を作る
* @var String selected 表示時に最初に選択されるキー
* @var String noValueText valueが空文字列で、渡された文字列の選択肢を先頭に追加する
*/
Vue.component('v-select', {
props: ['select', 'name', 'selected', 'noValueText'],
template: `
<select :id=name :name=name>
<option value="" v-if="noValueText !== undefined">{{noValueText}}</option>
<option v-for="(op,key) in select" :value="key" :selected="selected !== undefined && selected === key">{{op}}</option>
</select>
`
});
let vue = new Vue({
el: "#vueEnabled",
data: {ready: false}
});
ELEMENT.locale(ELEMENT.lang.ja);
</script>
<style>
.v-table {
border: solid 1px<?=$bordercolor?>;
}
.v-table td {
border-top: solid 1px<?=$bordercolor?>;
}
.v-table tr {
width: 100%;
}
.v-table tr:last-of-type td {
border-bottom: solid 1px<?=$bordercolor?>;
}
.v-table td {
border-left: solid 1px<?=$bordercolor?>;
}
.v-table td:last-of-type {
border-right: solid 1px<?=$bordercolor?>;
}
[v-cloak] {
visibility: hidden;
}
.modalImage {
width: 100%;
}
.el-message-box {
max-width: 80%;
max-height: 80%;
}
.el-message-box * {
text-shadow: none;
}
.el-message-box input {
height: inherit;
}
</style>
<?php
/**
* デバッグ カード検索 (vue.js)
*
* クロ庭仕様
*
* @var mixed[] $weapons
* @var string[] $requested
*/
$this->setPageTitle($title = 'カードリスト');
?>
<div id="vueEnabled" v-if="ready">
<!-- #vueEnabled の中だけvueコンポーネントは有効 -->
<!-- 検索条件 -->
<form action="" method="post">
<table>
<tr>
<td><label for="name">ID/名前</label></td>
<td colspan="3"><input type="text" name="name" id="name" v-model="form.name"/></td>
</tr>
<tr>
<td><label for="rarity">レア度</label></td>
<td>
<v-select name="rarity" :select="enums.rarity" :selected="form.rarity" no-value-text="---"/>
</td>
<td><label for="type">属性</label></td>
<td>
<v-select name="type" :select="enums.type" :selected="form.type" no-value-text="---"/>
</td>
</tr>
<tr>
<td><label for="type2">素質</label></td>
<td>
<v-select name="type2" :select="enums.type2" :selected="form.type2" no-value-text="---"/>
</td>
<td><label for="type4">得意距離</label></td>
<td>
<v-select name="type4" :select="enums.type4" :selected="form.type4" no-value-text="---"/>
</td>
</tr>
</table>
<input type="submit" value="検索">
</form>
<div v-cloak>
<span>{{weapons.length}}件</span>
<!-- v-table コンポーネント呼び出し -->
<!-- vue.weapons を v-table.data に、vue.template を v-table.struct に渡している -->
<v-table :data="weapons" :struct="template" width="100%"/>
</div>
</div>
<?php
/* vue コンポーネント読み込みは #vueEnabled のあとに行うこと */
$option = [
"bordercolor" => " #333"
];
$this->outputUnit('/parts/vue_components', $option);
?>
<script>
/**
* 定数変換オブジェクト
* v-table用structのtext用
*/
const enums = {
rarity: {
1: 'N',
2: 'R',
3: 'SR',
4: 'GR',
5: 'UR',
8: 'DR'
},
type: {
101: "",
102: "",
103: "",
104: ""
},
type2: {
1: "慈愛",
2: "武勇",
3: "自然",
4: "神聖",
5: "暗黒",
6: "叡智",
},
type4: {
1: "前衛",
2: "中衛",
3: "後衛"
}
};
/**
* v-table カードリストテンプレート
* @see parts/vue_components.html.php v-table
*/
const template = [
{img: 'object.images', width: '100px', modal: true},
[
[{eText: 'object.name', colspan: 4}],
[{text: "id"}, {eText: 'object.id', colspan: 3}],
[{text: "レア度"}, {eText: 'object.rarity', enum: 'rarity'}, {text: "属性"}, {eText: 'object.type', enum: "type"}],
[{text: "素質"}, {eText: 'object.type2', enum: "type2"}, {text: "距離"}, {eText: 'object.type4', enum: "type4"}],
[{present: "'card:' + object.id", content: "object.name", colspan: 4, align: 'right'}],
[{slotStructName: 'vue.templateBattleSkills', slot: 'object.battleSkills', colspan: 4}]
]
];
/* インラインテンプレートも書式はルートテンプレートと同じ */
vue.templateBattleSkills = [
[{eText: 'object.name', colspan: 2}],
[{text: "id"}, {eText: 'object.id'}],
[{eText: 'object.story', colspan: 2}]
];
/* ルートテンプレートはvueにくっつける */
vue.template = template;
/* 定数変換をアサイン、セレクトボックス生成用にselectorOptionsに詰める */
vue.enums = enums;
/* weaponsとformの値を詰める */
vue.weapons = <?=json_encode($weapons)?>;
vue.form = <?=json_encode($requested)?>;
/* 最後にvue.ready = true にしたときに初回のレンダリングがされる */
vue.ready = true;
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment